endpointr / docs

Endpointr API

basehttps://api.endpointr.com
authBearer JWT
endpoints1317

Endpointr API

A self-hosted REST gateway. JWT Bearer auth on every call; per-customer encrypted vault for third-party credentials; auto-generated REST routes; first-class outbound + inbound webhooks; provider-failover for AI; stateless OAuth relay for user-scoped credentials.

One contract for everything you'd otherwise glue together yourself.

Quick start (60 seconds, zero to first 200)

1. Import this collection into Postman.
2. Edit the collection variables (right-click collection → *Edit* → *Variables*):
| Variable | Set to |
|---|---|
| baseUrl | https://api.endpointr.com (or http://localhost:8080 while developing) |
| api_key | Your customer's api_key (from scripts/create_customer.php or the admin UI) |
| token | Leave blank — auto-filled by the test script on the first POST /v1/token |
3. Run POST /v1/token in the *Auth* folder. The test script captures data.token into {{token}} and every other request inherits Authorization: Bearer {{token}}.
4. Set up a service — for example, OpenAI: PUT /v1/credentials/openai with body {"api_key":"sk-..."}. Done. Now POST /v1/ai/chat with {"prompt":"hi"} works.

That's the whole onboarding loop.

Postman variables (recommended)

The example bodies use {{double_brace}} placeholders for things only you can supply (OAuth tokens, account IDs, contact-book IDs). Define them once in your Postman environment and every example "just works." The most useful ones:

VariableWhere it's usedHow to get it
claude_oauth_tokenAI examples for provider: claude-cliclaude setup-token (Claude Max subscription)
missive_api_keyMissive passthrough modeMissive *Preferences → API → Create token* (missive_pat-…)
missive_account / missive_organization / missive_team / missive_user / missive_contact_book / missive_conversationMissive examples that reference those resourcesLook them up via the relevant GET /v1/mail/missive-* endpoint
google_oauth_tokenGoogle Tasks (direct access_token mode)OAuth flow (1h TTL); for testing, oauth playground
google_refresh_token / google_client_id / google_client_secretGoogle Tasks (refresh-triplet mode)Same OAuth playground; pick Tasks API v1 → tasks scope
meta_ad_account_idEvery /v1/marketing/* example with account_idGET /v1/marketing/ad-accounts after creds are set — pick one. Includes the act_ prefix.
meta_page_idLead-form / page examplesGET /v1/marketing/pages — the id of the Page you'll run lead-gen ads from.
meta_pixel_idConversions API examplesGET /v1/marketing/pixels?account_id={{meta_ad_account_id}}
meta_webhook_verify_tokenWebhook subscription examplesWhatever you set in the META_WEBHOOK_VERIFY_TOKEN env var (openssl rand -hex 32).

Anything you see as <placeholder> in a description (e.g. <conversation-id>) is shorthand for "fill this in" — usually a server-side ID you obtain by listing first.

Auth

  • POST /v1/token — issue (no auth; body {api_key})
  • PATCH /v1/token — rotate (revokes old jti, issues new)
  • DELETE /v1/token — revoke (logout)
  • POST /v1/keys — upload your PEM public key (≥ 2048 bits)
  • GET /v1/keys — fetch your stored public key

Tokens carry a jti checked against a revocation table on every request.

Credentials — vault, passthrough, hybrid

Most third-party credentials live in the encrypted service_credentials vault — set them once, never touch them again. Some integrations also let you supply credentials per-request so you can keep secrets entirely client-side.

Vault-only services

ServiceKeys
openaiapi_key, optional organization
anthropicapi_key
openrouterapi_key, optional referer, app_title
claude-clioauth_token (from claude setup-token)
openai-cliapi_key
stripeapi_key, optional webhook_secret
mailchimpapi_key (dc suffix in key), optional list_id
ipregistryapi_key
clearhausapi_key
meta-adsaccess_token, app_id, app_secret, optional business_id, optional api_version (default v24.0) — see Meta Marketing API — first-time setup for the full provisioning recipe

Hybrid services (vault OR per-request)

ServiceVault entryPer-request override field
AI providers (CLI variants)claude-cli.oauth_token / openai-cli.api_keyoauth_token / api_key in body
missivemissive.api_keyapi_key in body (write verbs) or query (read verbs)
google (Tasks)google.{client_id, client_secret, refresh_token}oauth_token OR same triplet in body (write verbs) / query (read verbs)
google-tagmanager (GTM)google-tagmanager.{client_id, client_secret, refresh_token}oauth_token OR same triplet in body (write verbs) / query (read verbs) — own slot (GTM consent carries edit + publish scopes); refresh_token minted by the admin page's "Connect Google Tag Manager" button
reddit-adsreddit-ads.{client_id, client_secret, refresh_token, user_agent?}oauth_token OR same triplet in body (write verbs) / query (read verbs) — see Reddit Ads API — first-time setup

Vault-only (handlers TBD)

ServiceVault entry
microsoft-graph{tenant_id, client_id, client_secret, refresh_token} — slot for upcoming Outlook / Calendar / To Do handlers; admins can stage credentials today, no API endpoint consumes them yet.

Google in vault mode. Store the refresh-triplet once; Endpointr exchanges it for a fresh access_token on every call. Per-request oauth_token is then optional — useful when a client already has a fresh access_token in hand.

When to pick which mode

  • Vault. Set-and-forget. Required for binding-driven automations (EventDispatcher) and inbound-webhook signature verification — those flows have no live caller to attach credentials to.
  • Per-request. Keeps secrets out of the server entirely. Good for browser/mobile clients with their own secret store, or environments where the credential rotates frequently.
  • Override-on-vault. Both modes coexist for hybrid services — the per-request value wins for that single call, the vault remains the fallback. The override field is stripped before the upstream call so it never leaks into Missive's payload, query strings, or webhook event records.

When you add credentials for an AI provider, that provider is auto-appended to the customer's provider priority list — see below.

Handler URL derivation

Handlers auto-register at /v1/<subdir>/<kebab-class-name>. Example: class StripeCustomersHandler in handlers/payments/ → /v1/payments/stripe-customers.

REST verbs map to handler methods:

Method / pathHandler method
GET /v1/<g>/<name>getAll(customerId)
GET /v1/<g>/<name>/{id}get(customerId, id)
GET /v1/<g>/<name>/?…getByParam(customerId, query)
POST /v1/<g>/<name>create(customerId, data)
PUT /v1/<g>/<name>/{id}update(customerId, id, data)
DELETE /v1/<g>/<name>/{id}delete(customerId, id)

Only the verbs the handler implements are reachable. Unimplemented verbs return HTTP 500 with a descriptive error.

Response envelope

Every response is wrapped:

{
  "httpCode":  200,
  "status":    "HTTP/1.1 200 OK",
  "timestamp": "2026-04-21T12:00:00+00:00",
  "requestId": "9f3d8e1a2b…",
  "version":   "1.0.0",
  "data":      { /* handler return value */ }
}

X-Request-Id header mirrors requestId — include it in bug reports.

Webhooks

Every handler operation fires a typed event (e.g. StripeCustomersHandler.create, MissiveDraftsHandler.create). Register a listener with POST /v1/webhooks; the response returns a one-time secret. Receivers verify HMAC-SHA256 against the JSON body via the X-Endpointr-Signature: sha256=… header.

Sensitive fields (api_key, oauth_token, password, secret, etc.) are auto-redacted from webhook payloads — your hybrid-credential override never reaches subscribers.

AI handlers — unified multi-provider

Five endpoints, five providers. Pick one per request via the provider field, or leave it off and let the customer's priority list decide.

ProviderAuthChatVisionImage genStreamModels
openaiper-customer api_key✅✅✅✅✅
anthropicper-customer api_key✅✅—✅✅
openrouterper-customer api_key✅✅✅✅✅
claude-clioauth_token (Claude Max sub)✅✅—✅—
openai-cliserver-side codex login (sub)✅————

CLI providers ride one shared subscription on this server; API providers bill to the customer's own account.

### Endpoints
- POST /v1/ai/chat — text
- POST /v1/ai/conversation — text + server-side memory + per-customer Elasticsearch retrieval (narration / TTS tone)
- POST /v1/ai/vision — image-in + text-out
- POST /v1/ai/image — image generation (openai, openrouter)
- POST /v1/ai/stream — SSE chat stream
- POST /v1/ai/upload — upload an image, get a short-lived public URL for vision providers
- GET /v1/ai/models/?provider=… — list available models
- POST /v1/chat/completions (alias: POST /v1/ai/completions) — OpenAI-compatible, see below

model is always passed via the model field — no hardcoded default outside provider fallbacks.

OpenAI-compatible endpoint (/v1/chat/completions)

Drop-in replacement for OpenAI's POST /v1/chat/completions. Built for n8n's *OpenAI* credential, LangChain's ChatOpenAI, the official openai SDK, and anything else that speaks the OpenAI wire format — point them at {{baseUrl}}/v1 and your customer api_key works as the OpenAI key.

Difference from the rest of /v1/*Why
Auth: Authorization: Bearer <api_key> — the raw customer api_key, not a JWT.n8n's OpenAI credential has no refresh hook; flat keys is what OpenAI does too.
No response envelope. Body is the raw OpenAI shape ({id, object, created, model, choices, usage}); errors are {error: {message, type, code}}.LangChain / OpenAI SDK reads these fields directly.
Registered at two paths — /v1/chat/completions (drop-in) and /v1/ai/completions (namespace match).Same handler; pick whichever lines up with your client.

Model routing. The model string picks the upstream:

Model prefixUpstreamNotes
anthropic/… or claude-*Anthropic (vault anthropic.api_key)Full translation: tools ↔ input_schema, tool_use ↔ tool_calls, system extraction, stop_reason mapping.
openai/… or gpt-* / o1-* / o3-* / o4-*OpenAI (vault openai.api_key)Near-passthrough.
anything elseOpenRouter (vault openrouter.api_key)Model string passed verbatim (google/gemini-2.5-flash, etc.).

Missing vault creds for the resolved upstream → 401 with a remediation hint.

Tool calling. Required for n8n's AI Agent node. Send OpenAI-shape tools + tool_choice. The response carries finish_reason: "tool_calls" with tool_calls[].function.arguments as a JSON-encoded string (do not parse server-side — clients reassemble). Continue the loop with {role: "tool", tool_call_id, content}. Parallel tool calls supported.

Streaming. "stream": true → text/event-stream with chat.completion.chunk frames and data: [DONE] sentinel. Anthropic streams are translated event-by-event; OpenAI / OpenRouter streams are near-passthrough.

Accepted but ignored (so n8n payloads don't trip a 400): logprobs, top_logprobs, n, seed, logit_bias, user, service_tier.

Setup recipe. One-time for the customer:
1. Generate an api_key (admin UI or scripts/create_customer.php).
2. PUT /v1/credentials/anthropic (and/or openai, openrouter) with the upstream provider's key.
3. In n8n: create an *OpenAI* credential → API Key = customer api_key, Base URL = https://api.endpointr.com/v1.

That's the whole loop. The customer's api_key is now their single credential for every n8n LLM node, AI Agent included.

Default models (when you omit model)

ProviderChat / Vision / StreamImage gen
anthropicclaude-sonnet-4-6—
claude-cliclaude-sonnet-4-6—
openaigpt-4o-minigpt-image-1
openai-cligpt-5—
openrouteropenai/gpt-4o-minigoogle/gemini-2.5-flash-image-preview

Provider priority & failover

provider is optional on every AI endpoint. When it's omitted, the resolver consults the customer's provider priority list — an ordered slug array stored on customers.provider_priority and managed in the admin UI at /admin/customers/{id} → *Provider priority*.

Resolution is capability-aware:

1. Walk the priority list top-to-bottom.
2. Skip any slug that lacks credentials in service_credentials.
3. Skip any slug whose capability map doesn't include the requested operation (so claude-cli is skipped for image/models, openai-cli is skipped for everything except chat, etc.).
4. The first survivor handles the request. If 2+ survive, they're wrapped in a failover chain.

Failover (chat/vision/image/models): if the chosen provider returns 5xx, 401, 403, or 429, the next survivor is tried. Other 4xx responses surface immediately — they're the caller's fault and the next provider would also reject the same payload.

Failover (stream): none. Once SSE headers are out, the connection is committed. The first qualifying candidate handles the stream; mid-stream errors surface as SSE error frames, not retries.

Explicit provider always wins. Passing provider in the body bypasses the priority list and uses exactly that provider with no failover — useful when you specifically want to route a request to OpenAI's gpt-image-1 or OpenRouter's google/gemini-3-pro-image-preview ("Nano Banana Pro").

Image generation — model picker

ProviderModelNotes
openaigpt-image-1Default OpenAI image model
openaidall-e-3Older DALL·E
openroutergoogle/gemini-2.5-flash-image-preview"Nano Banana" — fast & cheap
openroutergoogle/gemini-3-pro-image-preview"Nano Banana Pro" — higher quality

OpenRouter image responses are normalized to {b64_json: "…"} so callers can swap providers without reshaping output.

Meta Marketing API — first-time setup

Endpointr's /v1/marketing/* endpoints are a thin canonicalised facade over Meta's Graph API. To use them you need a Meta App + a long-lived access token + an app_secret — none of which Endpointr can mint for you. This section is the end-to-end recipe.

Time budget: ~30 minutes for a brand-new Meta account; ~10 minutes if you already have a Business Portfolio.

Glossary (just enough to follow the steps)

TermWhat it is
Meta AppThe "client" Endpointr identifies as when calling Graph. Has an app_id + app_secret. Created at developers.facebook.com/apps.
Business Portfolio (formerly Business Manager)A container that owns ad accounts, Pages, pixels, and people. Created at business.facebook.com. The portfolio's id is the business_id credential.
System UserA non-human user inside a Business Portfolio that owns access tokens. Tokens minted by a System User never expire (vs. ~60 d for human-user tokens) and survive password resets. This is what production should use.
Access tokenThe bearer string Endpointr sends on every Graph call. Stored at service_credentials.meta-ads.access_token.
Verify tokenThe shared secret Meta echoes during webhook subscription handshakes. Set app-wide via the META_WEBHOOK_VERIFY_TOKEN env var.

Step 1 — Create the Meta App (5 minutes)

1. Go to developers.facebook.com/apps → Create app.
2. Use case: pick *Other* → Next.
3. App type: *Business* → Next.
4. Name / contact email: anything descriptive. Attach a Business Portfolio if you already have one (otherwise create one inline).
5. After creation, on the App Dashboard → Add products → click Set up on:
- Marketing API (required)
- Webhooks (only if you want inbound events — leadgen, account-status changes, etc.)
6. Open App Settings → Basic and grab:
- App ID → app_id in the credential
- App Secret (click *Show*) → app_secret in the credential

The app starts in Development mode, which is fine — Marketing API works in dev mode for any ad account the app owner / a System User has access to. You only need to flip to *Live* if you want third parties to log in via this app.

Step 2 — Create a Business Portfolio + System User (10 minutes)

Skip if you already have a Business Portfolio with a System User that owns the ad accounts you'll be managing.

1. Go to business.facebook.com → Create account. Fill in the legal name + your contact email.
2. Inside the new portfolio → Settings (⚙) → Business Settings.
3. Users → System Users → Add. Name it something like endpointr-api. Role: Admin.
4. With the System User selected → Add Assets → Apps → tick the app you created in Step 1 → grant Full control.
5. Add Assets again → Ad Accounts → tick every ad account you want Endpointr to manage → grant Manage ad account.
6. Repeat for Pages (needed for lead-gen ads + Page-attached creatives) and Pixels (needed for Conversions API).
7. Copy the Business Portfolio ID from *Business Settings → Business Info* → that's the business_id credential (optional — leaving it blank means Endpointr discovers ad accounts via /me/adaccounts instead of /{business_id}/owned_ad_accounts; the latter is recommended for tokens that own many accounts).

Step 3 — Generate a long-lived System User access token (3 minutes)

Still in *Business Settings → Users → System Users*:

1. Click the System User you created → Generate new token.
2. App: pick the app from Step 1.
3. Token expiration: *Never*.
4. Permissions (tick all of these — missing scopes silently break specific endpoints):

| Scope | Used for |
|---|---|
| ads_management | All write operations on campaigns, ad sets, ads, creatives |
| ads_read | All read operations + insights |
| business_management | business_id-scoped account discovery, catalogs |
| leads_retrieval | /v1/marketing/leads |
| pages_show_list | /v1/marketing/pages |
| pages_read_engagement | Lead-gen webhook + Page-attached creatives |
| pages_manage_metadata | Subscribing the Page to webhooks |
| instagram_basic *(optional)* | Instagram ads via the connected IG business account |

5. Click Generate token. Copy it now — Meta only shows it once. That's the access_token credential.

Step 4 — Store the credentials in Endpointr (1 minute)

PUT {{baseUrl}}/v1/credentials/meta-ads
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "access_token": "EAA...the-long-string-from-step-3",
  "app_id":       "1234567890123456",
  "app_secret":   "abcdef0123456789abcdef0123456789",
  "business_id":  "9876543210987654",
  "api_version":  "v24.0"
}

api_version is optional — defaults to whatever META_DEFAULT_API_VERSION (env) is set to (v24.0 out of the box). Pin per-customer when one partner needs an older version.

Step 5 — Smoke-test (30 seconds)

GET {{baseUrl}}/v1/marketing/auth
Authorization: Bearer {{token}}

Expected response:

{
  "data": {
    "debug_token": {
      "app_id":       "1234567890123456",
      "type":         "SYSTEM_USER",
      "expires_at":   0,
      "is_valid":     true,
      "scopes":       ["ads_management", "ads_read", "business_management", "..."]
    },
    "scopes": {
      "granted":  ["ads_management", "ads_read", "..."],
      "declined": []
    }
  }
}

If expires_at is not 0, you didn't generate a System User token — short-lived tokens will work for testing but die in ~60 days. If scopes.granted is missing one of the rows from Step 3, go back to the System User and add the missing permission.

Then list your accounts:

GET {{baseUrl}}/v1/marketing/ad-accounts

Pick one of the act_… ids from the response — that's your {{meta_ad_account_id}} Postman variable from here on.

Step 6 — (optional) wire up webhooks

If you want Meta to push events (leadgen submissions, ad-account status changes), you also need:

1. Set the META_WEBHOOK_VERIFY_TOKEN env var server-side. Generate with openssl rand -hex 32. This is one app-wide value — Meta echoes it during the handshake at GET /v1/webhooks/inbound/meta-ads.

2. In the Meta App Dashboard → Webhooks, add the same value as the *Verify Token* and https://api.endpointr.com/v1/webhooks/inbound/meta-ads as the *Callback URL*. Subscribe to the objects you care about (page, ad_account, etc.). Meta will hit the GET endpoint immediately to validate the token.

3. Subscribe to specific fields via Endpointr's wrapper (uses the same callback URL by default and persists the local mapping so inbound deliveries can be attributed to the right customer):

   POST {{baseUrl}}/v1/marketing/webhook-subscriptions
   Content-Type: application/json

   {
     "object":       "page",
     "object_id":    "{{meta_page_id}}",
     "fields":       ["leadgen"]
   }
   

4. Test the inbound endpoint from Meta App Dashboard → Webhooks → Test → pick leadgen. A row will appear in the meta_ad_webhook_events table within a second, and a leadgen_fetch job will be queued for the meta marketing worker. The full lead body lands in meta_ad_leads; subscribed MetaAds.page.leadgen outbound webhooks fire too.

Common gotchas

  • (#100) Param account_id… not supported — you forgot the act_ prefix on an account id. Endpointr auto-prefixes when you pass it as account_id in a body, but some Graph error paths surface the raw error before that normalisation. Always include act_ to be safe.
  • (#10) You do not have permission to perform this action — the System User isn't assigned the relevant asset (ad account / Page / pixel) with *Manage* role. Back to Step 2.5/2.6.
  • (#190) Error validating access token: Session has expired — short-lived token; regenerate as a System User token (Step 3, expiration *Never*).
  • (#368) The action attempted has been deemed abusive — usually Meta thinks the request is bot-generated. Make sure the System User token was generated against the same app you're calling from, and that appsecret_proof is on (it is by default in MetaGraphClient when app_secret is set in the vault).
  • Conversions API events don't show up in Events Manager — they take ~20 minutes to appear in the test events tab unless you pass test_event_code: 'TEST123…' (from Events Manager → *Test Events*). Production events appear under the normal aggregations after that delay.
  • Video upload stuck at processing forever — Meta's transcoder can occasionally fail silently. Re-upload with a different container/codec (H.264 + AAC in MP4 is the safest). The worker stops polling after 30 attempts (~5 minutes) and marks the asset failed with the last status payload.

Reddit Ads API — first-time setup

Full campaign management against the Reddit Ads API v3 (ads-api.reddit.com/api/v3) under /v1/redditads/*. Reading works for any developer app; endpoints that create/edit ads additionally require Reddit's Ads API approval — apply at redditforbusiness.com/api-partnership or via your Reddit account team.

Step 1 — Create the Reddit app (3 minutes)

1. Log in to Reddit with the account that manages the ads (or a dedicated integration account) and open reddit.com/prefs/apps.
2. Create app → type web app.
3. Set the redirect uri to the exact value shown next to the "Connect Reddit Ads" button on the customer's Endpointr admin page (https://<your-host>/admin/reddit/oauth/callback). Reddit allows exactly one redirect URI per app and requires an exact match.
4. Note the client_id (the string under the app name) and client_secret.

Step 2 — Store the credentials (1 minute)

Admin UI: customer page → add a reddit-ads credential with client_id + client_secret (leave user_agent blank for Endpointr's default — Reddit requires a descriptive User-Agent and blocks generic ones; set your own only if you want your app identified differently, format <platform>:<app id>:<version> (by /u/<username>)).

Or over the API:

PUT /v1/credentials/reddit-ads
{"client_id": "…", "client_secret": "…"}

Step 3 — Connect (30 seconds)

Click Connect Reddit Ads → on the customer's admin page. Reddit shows a consent screen requesting the adsread adsedit adsconversions adsdatadeletion scopes with duration=permanent; on approval the refresh_token is minted and written into the vault automatically — never type it by hand. (Escape hatch: if you already have a refresh_token from elsewhere, PUT the full triplet instead.)

Endpointr then exchanges the triplet for a short-lived (~24 h) access_token on every call, and persists any rotated refresh_token so the connection self-sustains. Vault-minted tokens are surfaced as refreshed_access_token in responses for client-side caching.

Step 4 — Smoke-test (30 seconds)

GET /v1/redditads/reddit-me            → the authenticated member
GET /v1/redditads/reddit-businesses    → business_id source
GET /v1/redditads/reddit-ad-accounts?business_id=<id>
GET /v1/redditads/reddit-campaigns?ad_account_id=<id>

Campaign build order

campaign (needs funding_instrument_id from /v1/redditads/reddit-funding-instruments?ad_account_id=…) → ad group (targeting/bid; validate values via /v1/redditads/reddit-targeting?resource=…) → post (profile_id from /v1/redditads/reddit-profiles?ad_account_id=…) → ad (binds post_id to ad_group_id). Create everything configured_status: PAUSED, flip to ACTIVE when ready.

Common gotchas

  • Write bodies are {"data": {...}} upstream — Endpointr wraps flat fields automatically; only pass an explicit data key if you're sending Reddit's wire shape yourself.
  • Updates are PATCH upstream — Endpointr's PUT /v1/redditads/…/{id} forwards as PATCH; send only the fields to change.
  • No delete for campaigns / ad groups / ads — archive or pause via {"configured_status": "PAUSED"|"ARCHIVED"}. DELETE exists only for custom audiences, product catalogs/feeds/sets.
  • Money is microcurrency (1,000,000 = 1 unit of the account currency); timestamps ISO 8601 UTC.
  • Pagination — lists take page.size/page.token (aliases page_size/page_token work around PHP's $_GET dot-mangling) and return pagination.next_url; pass that back as next_url to follow it.
  • 401/403 on writes with valid reads — the app lacks Reddit's Ads API developer approval (see the intro), or the token is missing the adsedit scope (re-connect).
  • invalid_grant on token refresh — the refresh_token was revoked (password change, app deauthorised) — click "Connect Reddit Ads" again.

Stubs — endpoints present but dependency-missing

EndpointInstall
/v1/rendering/html2-pdfcomposer require dompdf/dompdf
/v1/rendering/phantom-jsdeprecated; migrate to Playwright externally

These throw a clear RuntimeException explaining what to install.

Rebuilding this collection

When you add a handler or change an AI provider's behaviour, re-run:

php v1/generate_postman.php
php scripts/generate_docs.php

The first rewrites postman_collection.json (re-import into Postman or use *Update* on the existing collection). The second rewrites public/documentation/index.html from the same JSON, so both stay in sync.

MCP Servers

Every handler is also reachable as an MCP tool — one tool per (handler, verb) pair, named endpointr_<group>_<resource>_<verb>. Handlers are grouped into dedicated connectors below so a client only sees the tools it needs; the endpointr connector is the catch-all. Auth is OAuth2 + PKCE with Dynamic Client Registration — the consent screen asks for your api_key (the same one used at POST /v1/token), so there's no separate identity stack. Paste any connector URL into claude.ai/settings/connectors, or add it to Claude Code with the command on each card below.

MCPendpointr — https://mcp.endpointr.com
every handler except the dedicated connectors below

endpointr is a multi-tenant REST gateway. Each tool here mirrors one HTTP
endpoint under /v1/*. The tool name encodes the route shape:
endpointr_<group>_<resource>_<verb>
where verb is one of: list (GET collection), get (GET by id), query
(GET with query params), create (POST), update (PUT), delete (DELETE).

Tool arguments mirror the HTTP request body / path id / query string.
Most create/update tools accept an open body object — the schema lists
documented fields, but handlers commonly accept additional keys.

When the stored credential a call resolves carries an optional account_name
(e.g. distinguishing a "Conzent" Stripe account from a "Rexultz" one), the
response includes an _account object — {"name": …, "service": …} — naming
which account the call hit. Absent that field, no account_name was configured.

claude mcp add endpointr --transport http https://mcp.endpointr.com
MCPendpointr-tidycal — https://tidycal.calendars.mcp.endpointr.com
groups: tidycal

This connector is a TidyCal relay (https://tidycal.com/api) exposed through
endpointr — read AND write access to your TidyCal scheduling data (unlike
TidyCal's own read-only MCP). Each tool mirrors one TidyCal REST endpoint. Tool
names follow:
endpointr_tidycal_tidy_cal_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST/PATCH upstream), delete (DELETE).

Resources:
- me your account profile (list; GET /me).
- bookings query/get scheduled bookings; create books a slot on a
booking type (create REQUIRES booking_type_id in the
body, plus {name, email, timezone} and either starts_at
or a bookings[] array for packages).
- booking-cancel cancel a booking (create {booking_id, reason?}).
- booking-types query your booking types; create a new one
({title, description, duration_minutes, url_slug, …}).
- timeslots query bookable slots for a booking type — REQUIRES
booking_type_id, starts_at, ends_at (UTC).
- contacts query/create contacts.
- teams query/get teams you own or belong to.
- team-bookings query a team's bookings — REQUIRES team_id.
- team-users query/invite team members (create {team_id, email,
role_name?}); remove with a composite id teamId:teamUserId.
- team-booking-types query/create a team's booking types — REQUIRES team_id.
- accounts list the configured TidyCal account labels (see below).

MCP note: there is no query string over MCP, so resources that need a parent id
take it in the query arg (booking_type_id / team_id) for list verbs, in the
body for create, and as a composite parent:child id for get/delete.

Multiple TidyCal accounts. One login can hold several TidyCal accounts, each
stored under its own credential tidycal:<label> via
PUT /v1/credentials/tidycal:<label> {api_key:'…'} (e.g. tidycal:work). EVERY
tool (except accounts-list) accepts an optional top-level account argument
naming which account to target. When more than one account is configured,
account is REQUIRED — a call that omits it is rejected with the list of valid
labels. Call endpointr_tidycal_tidy_cal_accounts_list to see the labels. If only
a single unlabelled tidycal credential exists, account may be omitted.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream TidyCal token (a personal access token; API access needs a
paid/Pro plan) is resolved per customer from the credential vault — store it
once via PUT /v1/credentials/tidycal {api_key:'…'}. Alternatively pass
api_key inline in any tool's body/query for per-request passthrough; it is
stripped before the request reaches TidyCal.

Tool arguments mirror the HTTP request body / path id / query string. Most
create tools accept an open body object — the schema lists documented fields,
but handlers forward additional keys to TidyCal verbatim.

claude mcp add tidycal --transport http https://tidycal.calendars.mcp.endpointr.com
MCPendpointr-missive — https://missive.mcp.endpointr.com
classes: Missive*

This connector is a Missive relay. Each tool mirrors one Missive REST
endpoint (https://public.missiveapp.com/v1/) exposed through endpointr.
Tool names follow:
endpointr_mail_missive_<resource>_<verb>
where verb is one of: query (GET list with query params), get (GET by id),
create (POST), update (PUT/PATCH upstream), delete (DELETE).

Resources: conversations, messages, contacts, drafts, posts (internal
comments), tasks — plus the id-discovery lists: organizations, teams, users,
shared-labels, contact-books (each takes no parameters).

Where ids come from (chain these): conversation id ← conversations list (no
params; 25 most recent from the All mailbox) or query (mailbox filters);
message id ← messages query with conversation=<id>; contact_book ←
contact-books list; organization ← organizations list; team / user /
shared-label ids ← their lists. Draft and task ids come from their create
responses — Missive has no list endpoint for either (upstream limitation).

Authentication. The connector OAuth establishes which endpointr customer
you are. The upstream Missive API key (a missive_pat-… token) is resolved
per customer from the credential vault — store it once via
PUT /v1/credentials/missive {api_key:…}. Alternatively, pass api_key
inline in any tool's body/query for per-request passthrough; it is stripped
before the request reaches Missive.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists
documented fields, but handlers commonly accept additional keys.

claude mcp add missive --transport http https://missive.mcp.endpointr.com
MCPendpointr-meta — https://meta.mcp.endpointr.com
groups: marketing, messaging

This connector bundles Meta's (Facebook/Instagram) Graph APIs exposed through
endpointr: the Marketing API (ads) and the Conversations API (Messenger +
Instagram messaging). Each tool mirrors one REST endpoint. Tool names follow:
endpointr_marketing_<resource>_<verb> (Marketing API — /v1/marketing/*)
endpointr_messaging_<resource>_<verb> (Conversations API — /v1/messaging/*)
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (PUT), delete (DELETE).

Marketing API (group marketing, vault slot meta-ads):
- ad-accounts the ad accounts the token can see (list/get). business_id
in the credential scopes discovery to one Business Manager.
- campaigns / ad-sets / ads the campaign hierarchy — full CRUD.
- creatives / ad-images / ad-videos creative assets (image/video upload;
ad-videos supports sync + async upload).
- audiences custom + lookalike audiences.
- pages Facebook Pages the token manages.
- pixels datasets + the Conversions API (server-side events).
- catalogs Commerce product catalogs.
- leads lead ads + lead forms.
- insights performance metrics (spend, impressions, actions, ROAS).
- auth Meta token introspection (debug-token, granted scopes) and
short-lived -> long-lived extension (create {action:'extend'}).
- webhook-subscriptions activate inbound meta-ads webhooks.

Conversations API (group messaging, vault slot meta-messaging):
- channels connected Pages / Instagram accounts (list/get). Listing
discovers each Page's access token and caches it.
- conversations list/read Messenger + Instagram threads (pass page_id,
platform=messenger|instagram).
- messages read messages (query by conversation_id) AND the Send
API (create): text, attachments, templates, quick
replies, plus sender actions (typing_on/typing_off/
mark_seen via {action,page_id,recipient_id}).
- profile look up a messaging user's public profile by PSID/IGSID
(pass page_id).
- messenger-profile persistent menu, ice breakers, greeting, get-started
button per Page (query/create/delete).
- webhook-subs activate inbound meta-messaging webhooks per Page
(subscribed_apps).
- token introspect / extend the stored messaging Meta token.

Authentication. The connector OAuth establishes which endpointr customer you
are. The two surfaces use SEPARATE vault slots (a login can wire either or
both), each resolved per customer from the credential vault:
- Marketing: PUT /v1/credentials/meta-ads {access_token, app_id, app_secret,
business_id?, api_version?}
. Prefer a long-lived System User token (or
extend a short-lived user token via marketing/auth {action:'extend'}).
- Messaging: PUT /v1/credentials/meta-messaging {access_token, app_id,
app_secret}
. The token needs messaging scopes (pages_messaging,
pages_show_list, pages_manage_metadata, pages_read_engagement, and for
Instagram instagram_basic + instagram_manage_messages, plus
business_management). Per-Page access tokens are auto-discovered from
/me/accounts and cached encrypted — pass page_id on
conversation/message/profile calls so the right Page token is used.
app_secret backs HMAC verification of inbound webhook deliveries and token
extension. api_version pins the Graph version (default v24.0) per customer.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Meta verbatim.

claude mcp add meta --transport http https://meta.mcp.endpointr.com
MCPendpointr-cloudflare — https://cloudflare.mcp.endpointr.com
groups: cloudflare

This connector is a Cloudflare API relay (https://api.cloudflare.com/client/v4)
exposed through endpointr, scoped to managing zones (domains), DNS, Workers,
and Pages. Each tool mirrors one REST endpoint under /v1/cloudflare/*. Tool
names follow:
endpointr_cloudflare_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (PUT/PATCH), delete (DELETE).

Resources:
- zones list/get/create/update/delete zones. Creating a zone is
how you ADD A NEW DOMAIN ({name, account_id?}). query
filters by name/status/account_id.
- dns-records DNS records, scoped to a zone. query lists them and
REQUIRES zone_id. create/update carry zone_id in the
body. get/delete take a composite id zoneId:recordId.
- workers Worker scripts (account-scoped). list/get/delete by
script name; create uploads an ES-module worker
({name, script, compatibility_date?, bindings?}).
- worker-routes route patterns that run a Worker, scoped to a zone.
query REQUIRES zone_id; delete takes zoneId:routeId.
- pages-projects Cloudflare Pages projects (account-scoped). list/get/
create/delete by project name.
- pages-deployments deployments per project. query REQUIRES project_name;
create triggers a deploy ({project_name, branch?});
get/delete take a composite id projectName:deploymentId.
- cf-accounts list/get the accounts the token can access — use this to
discover the account_id Workers/Pages need.
- token-verify verify the stored API token is active.
- cf-account-labels list the credential LABELS configured for this customer
(one per company) — the values the account selector
takes. Takes no selector itself.
- cache-purge clear a zone's cache. create REQUIRES zone_id in the
body plus one mode: purge_everything:true, or a
files / tags / hosts / prefixes array (the last
three are Enterprise-only).
- cache-settings read/update a zone's cache config. query REQUIRES
zone_id and returns cache_level / browser_cache_ttl /
development_mode / always_online / sort_query_string_for_cache.
update takes the setting name as the id and
{zone_id, value} in the body (development_mode on/off,
cache_level aggressive/basic/simplified, browser_cache_ttl
in seconds).

MCP note: there is no query string over MCP, so resources that need a parent id
take it in the query arg (zone_id / project_name) for list verbs, in the
body for create/update, and as a composite parent:child id for get/delete.

Multiple Cloudflare accounts. One login can hold several Cloudflare logins
(one per company, e.g. SitePoint Systems and Conzent), each stored under its
own credential cloudflare:<label> via PUT /v1/credentials/cloudflare:<label>
{api_token, account_id}
(e.g. cloudflare:conzent). EVERY tool (except
cf-account-labels) accepts an optional top-level account argument naming
which login to use. When more than one label is configured, account is
REQUIRED — a call that omits it is rejected with the list of valid labels. Call
endpointr_cloudflare_cf_account_labels_list to see the labels. If only a single
unlabelled cloudflare credential exists, account may be omitted. Do not
confuse the two "accounts": account (the selector) picks the stored token;
account_id is Cloudflare's own account inside that login (see cf-accounts).
When the stored credential carries account_name, responses include
_account naming which login the call hit.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream Cloudflare token is resolved per customer from the credential
vault — store it once via PUT /v1/credentials/cloudflare
{api_token, account_id}
, or per company as cloudflare:<label> (see above).
Only API Token (Bearer) auth is supported; the token needs the relevant Zone /
Workers / Pages permissions. account_id is the default account for
account-scoped resources (Workers, Pages, accounts) and may be overridden
per-request via account_id.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Cloudflare verbatim.

claude mcp add cloudflare --transport http https://cloudflare.mcp.endpointr.com
MCPendpointr-openprovider — https://openprovider.mcp.endpointr.com
groups: openprovider

This connector is an OpenProvider relay (https://api.openprovider.eu/v1beta)
exposed through endpointr, scoped to domain registration and DNS. Served at
openprovider.mcp.endpointr.com. Each tool mirrors one OpenProvider REST endpoint
under /v1/openprovider/*. Tool names follow:
endpointr_openprovider_op_<resource>_<verb>
where verb is one of: query (GET with query params), get (GET by id),
create (POST), update (PUT), delete (DELETE).

Typical flow: op-customers create (get an owner handle) -> op-domain-check
(is the name free?) -> op-domains create (register) -> op-zones / op-dns-records
(set up DNS).

Resources:
- op-customers The reusable contact HANDLE (e.g. XX123456-XX) a domain
references as owner/admin/tech. Full CRUD; the id is the
handle string. create returns data.handle. You need at
least one before registering a domain. Required to create:
name, address, phone, email (see the tool's minimal body).
- op-domain-check create = check availability (+ price with with_price).
Body {domains:[{name,extension}], with_price?}. status
free = available, active = taken.
- op-domains query/get/create/update/delete domains. create with no
action REGISTERS ({domain:{name,extension}, owner_handle,
period, name_servers|ns_group}); create with action:'renew'
+ id + period renews. get/update/delete take the NUMERIC
domain id.
- op-zones DNS zones CRUD. id = the zone NAME (full domain, e.g.
example.com). get returns records inline. update is how you
EDIT/REMOVE records
— send them grouped under
records:{add,remove,replace,update} (records have no id;
matched by their {type,name,value} tuple).
- op-dns-records query = list a zone's records (name required = the domain).
create = APPEND records to a zone
({domain, records:[{type,name,value,ttl}]}). For edit/remove
use op-zones update.

MCP note: there is no query string over MCP, so list filters go in the query
arg; the path id goes in the id arg — a NUMERIC domain id (op-domains), a
customer HANDLE (op-customers), or a zone NAME/full domain (op-zones). op-dns-records
takes the zone in the body/query, not as a path id.

Every response is the OpenProvider envelope {code, data, desc, warnings} —
code:0 is success and the useful payload is under data.

Authentication. The connector OAuth establishes which endpointr customer you
are. OpenProvider issues a short-lived Bearer token from username/password; the
relay logs in, caches the token, and re-logs-in on expiry, so you never handle
tokens. Store the login once via PUT /v1/credentials/openprovider
{username, password}
(optional ip, default 0.0.0.0 = any IP — set it if the
account restricts API access by IP). Vault-only: credentials are never accepted
per-request.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to OpenProvider verbatim (so the
full CreateDomain / CreateCustomer / UpdateZone request shapes are supported).

claude mcp add openprovider --transport http https://openprovider.mcp.endpointr.com
MCPendpointr-calcom — https://calcom.calendars.mcp.endpointr.com
groups: calcom

This connector is a Cal.com API v2 relay (https://api.cal.com/v2) exposed
through endpointr, scoped to scheduling: event types, availability slots,
bookings, and availability schedules. Served at
calcom.calendars.mcp.endpointr.com. Each tool mirrors one REST endpoint under
/v1/calcom/*. Tool names follow:
endpointr_calcom_cal_com_<resource>_<verb>
where verb is one of: query (GET with query params), get (GET by id),
list (GET collection), create (POST), update (PUT/PATCH), delete (DELETE).

Typical flow: event-types (find an eventTypeId) -> slots (find an open time)
-> bookings create -> bookings query/get and cancel/reschedule.

Resources:
- event-types query = list bookable meeting types (filters: username,
eventSlug, orgSlug, ...). Each event type's numeric id is the
eventTypeId slots + bookings need. get = one by id.
- slots query = available times for an event type over a date range.
REQUIRES start + end (UTC, ISO 8601 / YYYY-MM-DD) AND an
event identifier (eventTypeId, or eventTypeSlug + username).
Optional timeZone, duration. Response data is grouped by
date.
- bookings query = list bookings (filters: status, attendeeEmail,
eventTypeId, limit, cursor, ...). get = one by its uid. create
= book a meeting ({start, eventTypeId, attendee:{name, email,
timeZone}}) OR act on an existing booking via an action field
+ bookingUid: action ∈ cancel | reschedule (needs new start)
| confirm | decline. Cal.com has no PUT/DELETE for bookings —
use create+action.
- schedules list/get/create/update/delete availability schedules (working
hours). create REQUIRES name + timeZone; isDefault
defaults false; availability defaults Mon-Fri 09:00-17:00.
- accounts list the configured Cal.com account labels (see below).

MCP note: there is no query string over MCP, so list filters go in the query
arg, the booking uid / schedule id / event type id go in the id arg (or, for
booking cancel/reschedule/confirm/decline, as bookingUid inside the create
body alongside action). The correct dated cal-api-version header is set by
the gateway per endpoint — you never send it.

Multiple Cal.com accounts. One login can hold several Cal.com accounts, each
stored under its own credential calcom:<label> via
PUT /v1/credentials/calcom:<label> {api_key:"cal_live_…"} (e.g. calcom:work).
EVERY tool (except accounts-list) accepts an optional top-level account
argument naming which account to target. When more than one account is
configured, account is REQUIRED — a call that omits it is rejected with the
list of valid labels. Call endpointr_calcom_cal_com_accounts_list to see the
labels. If only a single unlabelled calcom credential exists, account may be
omitted.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream Cal.com API key is resolved per customer from the credential
vault — store it once via PUT /v1/credentials/calcom {api_key:"cal_live_…"}
(test keys start cal_…), or per account as calcom:<label> (see above).
Alternatively pass api_key inline in any tool's body/query for per-request
passthrough; it is stripped before the request reaches Cal.com.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Cal.com verbatim.

claude mcp add calcom --transport http https://calcom.calendars.mcp.endpointr.com
MCPendpointr-dinero — https://dinero.mcp.endpointr.com
groups: accounting · classes: Dinero*

This connector is a Dinero accounting relay (api.dinero.dk) exposed through
endpointr. Each tool mirrors one Dinero REST endpoint. Tool names follow:
endpointr_accounting_dinero_<resource>_<verb>
where verb is one of: query (GET list / read sub-operation), get (GET by id),
list (GET collection), create (POST), update (PUT), delete (DELETE).

Resources: invoices (+ reminders), contacts (+ notes), products, accounts,
accounting-years, ledger-items, entries, vouchers (manual / purchase /
purchase credit notes), purchase-vouchers (payments), sales-credit-notes,
trade-offers, voucher-templates, reports, webhooks, integrations,
organizations (+ verification), state-of-account, business-goals, files,
attachments, sms, electronic-invoice, settings, countries, vat-types,
unified-vouchers.

Sub-operations and nested resources are folded onto an action field (and a
type field for the vouchers family) because the gateway publishes flat REST
routes only — e.g. book an invoice with create {action:'book', id:'…'}, read
a PDF with query {action:'pdf', id:'…'}. Each tool's description lists its
available actions and the ids they require. Reads and deletes of nested
resources (notes, reminders, payments, attachment files) are reached through
query/create with the relevant action, not the bare get/delete tool
(which only carries a single path id).

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream Dinero token uses Visma Connect OAuth2 (scopes
dineropublicapi:read dineropublicapi:write offline_access), minted by the
admin "Connect Dinero" flow and auto-refreshed by the relay against
connect.visma.com when it expires. Alternatively pass access_token +
organization_id inline on any call (per-request passthrough; both are
stripped before the request reaches Dinero).

Companies. One Visma login reaches several Dinero organisations, and each
one the admin exposed is a labelled company (e.g. sitepoint, conzent).
Every tool takes an optional top-level account argument naming the company
label. With exactly one company configured it may be omitted; with several
it is REQUIRED — the relay refuses to guess which company a call (and a
posting) lands in and lists the valid labels instead. Discover them with
endpointr_accounting_dinero_companies_list ({companies:[{label, name,
organization_id}], account_required}). Responses carry _account naming the
company that was hit. Passing organization_id inline bypasses the label.

Spend reports. endpointr_accounting_dinero_custom_reports_query aggregates a
company's ledger server-side for a period: totals with and without salary, by
type, by category, by account, by month, and (detail=detailed) the underlying
lines — so "what did we actually spend in August" is one call, not a pile of
raw entries. Every account in a report carries an ENGLISH type (income,
cost_of_goods, expense, salary, depreciation, financial_cost, tax, vat, bank,
receivable, payable, asset, liability, equity, other) with side (result |
balance) and type_source. The type comes from Dinero's own account
category (Salary, Administration Expenses, Vat Due, …, the same
grouping Dinero's result report uses), falling back to the standard chart by
account number; read the type, never guess from the account name, which
carries VAT-status suffixes like "med moms" that say nothing about the
account. action=chart returns the whole chart of
accounts classified the same way — read it once per company before trusting
numbers. Every report lists the accounts it EXCLUDED (with a reason) and the
ones it could not type (unmapped), so the definition can be tuned: save a
per-company definition with the ..._custom_reports_create tool
(account_types / category_types to fix a type, include_types to decide what
counts as spend, include/exclude ranges) and it applies to every later run for
that company. Use the raw entries and reports tools when you need Dinero's
own figures instead of the aggregation.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Dinero verbatim.

claude mcp add dinero --transport http https://dinero.mcp.endpointr.com
MCPendpointr-billy — https://billy.mcp.endpointr.com
groups: accounting · classes: Billy*

This connector is a Billy accounting relay (api.billysbilling.com/v2 — Billy
by Shine, mit.billy.dk) exposed through endpointr. Each tool mirrors one Billy
REST resource. Tool names follow:
endpointr_accounting_billy_<resource>_<verb>
where verb is one of: query (GET list / read sub-operation), get (GET by id),
list (GET collection), create (POST), update (PUT, partial), delete (DELETE).

Resources: invoices (+ invoice lines, late fees, reminders, reminder
associations), bills (+ bill lines), contacts (+ contact persons), products
(+ product prices), accounts (+ account groups, account natures), postings
and transactions (the ledger), daybooks and daybook transactions (+ lines:
manual journal entries), bank lines (+ matches, subject associations), bank
payments, attachments and files, balance modifiers, contact balance payments
and postings, tax rates (+ deduction components), sales tax accounts / meta
fields / payments / returns / rules / rulesets, organizations, users, and the
reference data (countries, country groups, currencies, cities, zipcodes,
states, locales).

Envelope. Billy answers with the record under a root key — a single record as
{"invoice": {...}}, a list as {"invoices": [...], "meta": {"paging": {...}}} —
and the relay returns that verbatim. A write returns EVERY record it changed
(creating an invoice line returns the recalculated invoice too). On create and
update send the record's fields directly; the relay wraps them in Billy's
singular root key and adds organizationId.

Lists page with page and pageSize (default and max 1000), sort with
sortProperty + sortDirection, sideload with include
(e.g. invoice.lines:embed,invoice.contact:sideload), search with q.
Filters are named query params; date periods take year:2026,
month:2026-09, quarter:2026-3, dates:2026-01-01...2026-03-31,
from:/through:. Billy IGNORES unknown filter names silently, so on
invoices, bills, postings, bank lines, attachments and daybook transactions
the relay rejects unknown filters with the allowed list.

Operations Billy models as state changes or sub-endpoints are folded onto an
action field:
- invoices create: approve (id) books a draft — the only state change
Billy allows; email (id + contactPersonId, emailSubject, emailBody);
credit_note (id of the invoice to credit); deliver (id) sends an
e-invoice via NemHandel/Peppol. invoices query: pdf (id) returns the
PDF as base64; logs (id) the e-invoice delivery log.
- bills create: approve (id), credit_note (id).
- daybook transactions create: approve (id) books a manual entry.
- files create uploads a file from content_base64; files query
download (id) returns the bytes.
- users query: me = the user behind the token.
Payments: register a payment on invoices or bills with bank-payments create
(cashSide debit = money in, credit = money out; associations name
invoice:<id> / bill:<id>).

Authentication. The connector OAuth establishes which endpointr customer you
are. Upstream, a Billy access token is permanent and tied to ONE
organisation, so each company is a credential row billy:<label> holding
{access_token, organization_id, account_name}. Alternatively pass
access_token (+ organization_id) inline on any call (passthrough; both
are stripped before the request reaches Billy).

Companies. Every tool (except the companies list) takes an optional
top-level account argument naming which company to target, by label. When
more than one company is configured, account is REQUIRED — a call that
omits it is rejected with the list of valid labels; the relay never guesses
which company's books a write lands in. Call
endpointr_accounting_billy_companies_list to see the labels (it also confirms
each token against Billy). If only one company is configured, account may
be omitted. Responses carry _account naming the company the call hit.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Billy verbatim.

claude mcp add billy --transport http https://billy.mcp.endpointr.com
MCPendpointr-economic — https://economic.mcp.endpointr.com
groups: accounting · classes: Economic*

This connector is an e-conomic (Visma e-conomic) accounting relay exposed
through endpointr. Each tool mirrors one e-conomic resource. Tool names
follow:
endpointr_accounting_economic_<resource>_<verb>
where verb is one of: query (GET list / read sub-operation), get (GET by id),
list (GET collection), create (POST), update (PUT), delete (DELETE).

Two upstream APIs behind one relay. The classic REST API
(restapi.e-conomic.com) carries the bulk: agreements/self, accounting years,
accounts, app roles, currencies, customer groups, customers, departmental
distributions, departments, employees, invoices (drafts → booked → sent /
paid / unpaid / overdue), orders, quotes, journals, layouts, payment terms,
payment types, product groups, products, supplier groups, suppliers, units,
VAT accounts, VAT zones. The per-domain APIs (apis.e-conomic.com) carry what
the classic API lacks: draft entries and BOOKING, booked entries (the
ledger), dimensions, documents (voucher attachments), webhooks, projects
(time, mileage), subscriptions, budgets, and the write side of accounts,
products, journals and suppliers' contacts.

Conventions. Classic lists answer {collection, pagination} and take
skippages + pagesize (max 1000), filter (prop$eq:value, chained with
$and:/$or:, operators $ne $gt $gte $lt $lte $like $in $nin, string values
escaped: $ ( ) * , [ ] as $$ $( $) $* $, $[ $]) and sort (-prop
descending). Domain lists are cursor-based ({cursor, items}, 1000 per page)
or paged (paged=true, pageSize ≤ 100, skipPages, sort) with count=true.
Domain writes: POST creates; PUT carries the record's id in the BODY and a
mandatory objectVersion (409 = stale, re-read and retry). Sub-resources
and lifecycle steps are folded onto action and kind (see each tool).

Booking. A draft invoice becomes booked with invoices create
{action:"book", id: <draft number>}. Manual entries are draft entries
(draft-entries create) booked with {action:"book"} (whole journal),
{action:"book_entries", entry_numbers:[…]} or {action:"book_interval",
from, to}. The ledger is booked-entries query (filter on date/account).

Authentication. The connector OAuth establishes which endpointr customer you
are. Upstream, every call carries our developer app's X-AppSecretToken
(stored once on the economic credential) and the company's
X-AgreementGrantToken (economic:<label>, one per agreement, minted by
"Connect e-conomic" in the admin). Alternatively pass app_secret_token +
agreement_grant_token inline on any call (passthrough; both are stripped
before the request). idempotency_key on a create/update is forwarded as
Idempotency-Key.

Agreements (companies). Every tool (except the agreements list) takes an
optional top-level account argument naming which company to target, by
label. When more than one agreement is configured, account is REQUIRED —
a call that omits it is rejected with the valid labels; the relay never
guesses whose books a write lands in. Call
endpointr_accounting_economic_agreements_list to see them (it also confirms
each grant against /self). Responses carry _account naming the company.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to e-conomic verbatim.

claude mcp add economic --transport http https://economic.mcp.endpointr.com
MCPendpointr-uniconta — https://uniconta.mcp.endpointr.com
groups: accounting · classes: Uniconta*

This connector is a Uniconta accounting relay (api.uniconta.com — the
Uniconta Web API) exposed through endpointr. Tool names follow:
endpointr_accounting_uniconta_<resource>_<verb>
where verb is one of: query (list / read sub-operation), get (by key),
list (no params), create (POST), update (PUT), delete (DELETE).

THE MODEL. Uniconta is a table store with posting APIs on top. Every table
(255 of them) is reachable through the entities tool — query with filters,
insert, update, delete — and the family tools (debtors, creditors, items,
accounts, transactions, vat, finance-years, payment-terms, employees,
projects, subscriptions, bank-statements, budgets, exchange-rates,
warehouses, contacts) are the same calls fixed to one table with its key
property. Keys are Uniconta's own: a debtor/creditor/GL account by
Account, an item by Item, an order by OrderNumber, an invoice by
InvoiceNumber, a journal by Journal; the entities tool addresses any
row as Entity:RowId.

Posting is separate from tables: journals (GLJournal: draft lines in a
daily journal, check, post, post_daily, settle_bank), orders
(action=generate_invoice BOOKS the invoice from a sales order and returns
its PDF; kind=creditor for purchases), invoices (posted invoices + PDFs),
settlements (open items; settle / reopen), project-journals,
inventory-journals, production, documents (attachments + the voucher inbox).

Filter grammar (the filters array of {property, value}): equality by
default; ranges 01-01-2025..31-05-2025 (dates are dd-MM-yyyy); lists
100;110;129; wildcards *abc*; order_by + descending; skip + take
page. Any other key on a query is an equality filter on that property.
Field names and types for any table: entities query with action=fields.

Authentication. The connector OAuth establishes which endpointr customer
you are. Upstream, the relay logs in with the customer's Uniconta
Server-Login user (credential uniconta {username, password}) and binds the
session to the selected company; the token is cached per company. Or pass
access_token (+ company_id) inline on any call (per-request
passthrough; both are stripped before the request reaches Uniconta).

Companies. A company is a labelled credential row uniconta:<label>
{company_id, account_name}. Every tool takes an optional top-level account
argument naming the label; required when more than one company is
configured. endpointr_accounting_uniconta_companies_list lists them (and
the companies the login can reach) and takes no selector.

Uniconta answers errors as HTTP 200 text; the relay turns them into
errors with a status (401 re-logs in once, 404, 409, else 422) and the
text. Nothing in this connector has run against a live Uniconta yet — the
first session pins the response shapes.

claude mcp add uniconta --transport http https://uniconta.mcp.endpointr.com
MCPendpointr-unified-accounting — https://unified-accounting.mcp.endpointr.com
groups: unified-accounting

This connector is endpointr's UNIFIED accounting API: one canonical surface
over every accounting system endpointr relays (Dinero, Billy, e-conomic,
Uniconta). Software binds a company to a provider once and then calls the
same tools whatever system is behind it. Tool names follow:
endpointr_unified_accounting_<resource>_<verb>
where verb is one of: query (GET list / read sub-operation), get (GET by id),
list (GET collection), create (POST), update (PUT, partial).

Resources: companies (the bindings — start here), capabilities (what each
provider supports), readiness (whether a company's books have what issuing
an invoice needs — ask it when a company is connected), accounts (the chart, every account with an English type),
accounting-years, customers and suppliers (email, phone and vat_number
are exact lookups — find a contact before creating it), products, quotes
(offers; an update replaces the whole quote and may return a new id), invoices (list/get/create
draft, then action: book, send, credit_note, register_payment; query
action=pdf), journal-entries (create draft, action: book, simulate),
entries (the ledger, debit-positive), vat-types, attachments, reports (the
spend report engine on any company).

Bindings. A binding is a credential row unified-accounting:<label>
{provider, account, account_name} pointing at that provider's own company
row (e.g. provider dinero, account conzent → dinero:conzent). It holds no
secret. EVERY tool (except companies) takes an optional top-level account
argument naming the binding; when more than one binding is configured it is
REQUIRED — a call that omits it is rejected with the valid labels. Call
endpointr_unified_accounting_companies_list to see them. Responses carry
_account (the binding) and _provider {provider, account, label} naming
which system answered.

Lowest common denominator, honestly. Every canonical record carries id (the
vendor id), provider and raw (the untouched vendor record) so nothing is
lost. What a provider cannot serve fails with error UNIFIED_UNSUPPORTED and
vendor_tool, the vendor connector's own tool that does it natively — call
that on the vendor connector for the edge case. Call
endpointr_unified_accounting_capabilities_query first when planning a
workflow: supported, partial (read the note), unsupported (use
vendor_tool), pending (the adapter is not built yet).

Account types. Accounts carry an English type — income, financial_income,
cost_of_goods, expense, salary, depreciation, financial_cost, tax, vat, bank,
receivable, payable, asset, liability, equity, other — from the shared
classification engine (definition overrides → the vendor's own category →
the standard Danish chart by number where the vendor uses it → the name).
Read type, never guess from a Danish account name. Ledger amounts are
DEBIT-POSITIVE on every provider: expenses positive, revenue negative.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream credentials stay on each provider's own credential rows;
the unified layer never holds a token.

Tool arguments mirror the HTTP request body / path id / query string.

claude mcp add unified-accounting --transport http https://unified-accounting.mcp.endpointr.com
MCPendpointr-analytics — https://analytics.mcp.endpointr.com
groups: searchconsole, analytics_data, analytics_admin

This connector bundles Google's measurement APIs — Search Console (read-only)
and Google Analytics 4 (Data reads + Admin reads AND writes) — exposed through
endpointr. Each tool mirrors one Google REST endpoint. Tool names follow:
endpointr_searchconsole_<resource>_<verb> (Search Console)
endpointr_analytics_data_ga4_<resource>_<verb> (GA4 Data API)
endpointr_analytics_admin_ga4_<resource>_<verb> (GA4 Admin API)
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (PATCH), delete (DELETE).

Google Tag Manager is a SEPARATE connector (endpointr-tagmanager) — container,
tag, trigger and publish work lives there, not here.

Search Console (https://www.googleapis.com/webmasters/v3):
- sites list verified sites (list); get one by ?siteUrl=.
- search-analytics create = run a Search Analytics query (clicks,
impressions, CTR, position) for a property. Requires
siteUrl + startDate + endDate; dimensions optional.
- sitemaps query = list a property's sitemaps (requires siteUrl;
add feedpath for a single sitemap).
- url-inspection create = inspect one URL's index status on a property
(requires inspectionUrl + siteUrl).

GA4 Data API (https://analyticsdata.googleapis.com/v1beta):
- ga4-reports create = runReport (metrics × dimensions over dateRanges).
- ga4-realtime create = runRealtimeReport (last 30 minutes).
- ga4-metadata query = discover the dimensions/metrics a property exposes.
All require property (accepts properties/123456 or a bare 123456).

GA4 Admin API (https://analyticsadmin.googleapis.com/v1beta) — discovery AND
configuration. This is the half that SETS TRACKING UP on an account that
already exists:

- ga4-account-summaries list = every account + property in one call. Start
here; you need no ids to make this call.
- ga4-accounts list = accounts the user can access. An account CANNOT be
created through any Google API (a human must accept the
Terms of Service), so this list is the ceiling —
everything below hangs off one of these.
- ga4-properties query (requires ?account=) / get / create / update /
delete. create needs {account, displayName, timeZone};
set currencyCode too — GA4 defaults to USD and past data
is not restated. delete is a 63-day soft delete.
- ga4-data-streams query (requires ?property=) / get / create / update /
delete. Creating a WEB_DATA_STREAM is what MINTS THE
MEASUREMENT ID (webStreamData.measurementId, G-XXXXXXXXXX)
— the value a Google tag or a GTM googtag tag needs.
- ga4-key-events the GA4 UI's "conversions". query/get/create/update/
delete; create = {property, eventName, countingMethod}.
The event need not have fired yet.
- ga4-custom-dimensions / ga4-custom-metrics register event parameters so
they are queryable. A parameter with no custom dimension
is collected and then INVISIBLE — the step most tracking
setups miss. Archive (not delete) via
create {action:'archive', id}.
- ga4-google-ads-links query/create/update/delete. create = {property,
customerId} where customerId is the 10-digit GOOGLE ADS
id. This is what lets key events import as Ads
conversions.
- ga4-data-retention per-property singleton, addressed by the PROPERTY
id. New properties default to TWO_MONTHS; FOURTEEN_MONTHS
is almost always what you want, and it is not
retroactive.
- ga4-enhanced-measurement a WEB stream's automatic events (scrolls,
outbound clicks, site search, video, downloads, form
interactions). get/update by propertyId:streamId.
v1alpha — no v1beta equivalent exists.
- ga4-measurement-protocol-secrets list/get/create/delete per data stream
(needs BOTH property and stream). The secret + measurement
ID are the credentials for SERVER-SIDE events.

THE ID MODEL for the Admin tools: collection verbs (query/create) take the
parent as property (accepting properties/123456 or a bare 123456) — plus
stream one level down for measurement-protocol secrets. get/update/delete take
the target as id, either the full resource name Google returns in name
(properties/123/keyEvents/456) or the MCP-friendly colon composite of the same
ids in order (123:456).

UPDATES ARE PATCHES. Google rejects a patch that does not name the fields it
changes, so send only the fields you are changing and the relay derives
updateMask from your body's top-level keys. Pass update_mask explicitly to
override — required when targeting a nested leaf, e.g.
{update_mask:'webStreamData.defaultUri', webStreamData:{defaultUri:'https://…'}}.

A NEW SITE, END TO END: ga4-accounts (pick the account) -> ga4-properties create
-> ga4-data-streams create (copy the measurement ID out of the response) ->
ga4-data-retention update -> ga4-key-events create (one per conversion) ->
ga4-custom-dimensions create (one per parameter your tags will send) ->
ga4-google-ads-links create. Then install the measurement ID on the site —
that part lives in the tagmanager connector, or in your own deploy.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream Google token is resolved per customer from the credential
vault — store a refresh-triplet once via PUT /v1/credentials/google
{client_id, client_secret, refresh_token}
and endpointr mints short-lived
access_tokens on every call. Alternatively pass oauth_token (a fresh
access_token) or the refresh-triplet inline on any call (per-request
passthrough; the credential fields are stripped before the request reaches
Google). The refresh_token must be minted with the scopes you need:
- https://www.googleapis.com/auth/webmasters.readonly (Search Console)
- https://www.googleapis.com/auth/analytics.readonly (Analytics Data+Admin reads)
- https://www.googleapis.com/auth/analytics.edit (GA4 Admin WRITES)

WRITING to the GA4 Admin API needs analytics.edit. Both Google connect flows
now request it: the shared google slot ("Connect with Google") and the
dedicated google-analytics slot ("Connect Google Analytics"), which exists for
customers who want GA4 write consent kept off the slot Tasks and Search Console
share. Every GA4 tool resolves its token as: credentials in the request bag ->
the google-analytics slot when the customer has one -> the google slot.

Widening the scope list grants nothing retroactively: a refresh_token minted
before analytics.edit was requested still carries only its original scopes, so a
write on such a grant comes back as Google's own 403 PERMISSION_DENIED until the
customer re-runs Connect once. Reads are unaffected either way.
When a token is minted from the vault, the new access_token is surfaced in the
response as refreshed_access_token so clients can cache it (~1h TTL).

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Google verbatim (so the full
runReport / searchAnalytics request shapes are supported).

claude mcp add analytics --transport http https://analytics.mcp.endpointr.com
MCPendpointr-google-ads — https://googleads.mcp.endpointr.com
groups: googleads

This connector is a Google Ads API (v25, REST) relay exposed through
endpointr: FULL account management — reads via GAQL, writes via each
resource's :mutate endpoint (one operation per call), plus the special
service methods (conversion uploads, keyword planning, recommendations,
account creation, Customer Match, experiments). Each tool mirrors one
REST endpoint under /v1/googleads/*. Tool names follow:
endpointr_googleads_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query
params), create (POST), update (PUT), delete (DELETE).

Typical flow: accessible-customers list (discover which Ads accounts you
can reach) -> any resource's query tool (canned GAQL id-discovery; every
row's resourceName is the id its update/delete take) -> create/update/
delete. reports create runs raw GAQL for anything the canned lists don't
cover.

THE ID MODEL (read this first): update/delete tools take the target as
id, in any of three forms — the full resource name exactly as query
tools return it (customers/<cid>/<resource>/<rid>), the composite
<customer_id>:<resource_id> (the MCP-friendly form; put the account's 10
digits before the colon), or a bare id when customer_id rides in the
update body. Google's own composite resource ids keep their ~ (an ad is
<adGroupId>~<adId>; an asset link <assetId>~<fieldType>).

Resources, grouped:
- Discovery & reporting: accessible-customers (list = every account the
OAuth grant reaches directly; empty usually means MCC-only access —
set login_customer_id to the manager and query customer_client via
reports), reports (create = raw GAQL SELECT, REQUIRES customer_id +
query; pageSize/pageToken page up to 10,000 rows).
- Campaign structure: campaign-budgets, ad-campaigns, ad-groups,
ad-group-ads, keywords (ad-group criteria of type KEYWORD).
- Targeting & bidding: campaign-criteria (geo/language/schedule/device/
negatives), ad-group-criteria (all criterion types),
customer-negative-criteria, shared-sets + shared-criteria +
campaign-shared-sets (negative keyword lists), labels +
campaign/ad-group/ad-group-ad-labels, bidding-strategies (portfolio),
campaign-bid-modifiers, ad-group-bid-modifiers.
- Assets: assets (permanent — NO delete; detach links instead),
asset-sets + asset-set-assets + campaign/ad-group-asset-sets, and the
link tables customer-assets / campaign-assets / ad-group-assets.
- Performance Max: asset-groups, asset-group-assets,
asset-group-listing-group-filters, asset-group-signals.
- Conversions & goals: conversion-actions, conversion-uploads (create
only; action = click|call|adjustments; Google requires
partialFailure:true), conversion-value-rules + -rule-sets,
conversion-custom-variables, custom-conversion-goals,
customer-conversion-goals + campaign-conversion-goals,
conversion-goal-campaign-configs, goals + campaign-goal-configs
(v25 unified goals).
- Audiences: user-lists, offline-user-data-jobs (Customer Match:
create job -> action add_operations -> action run -> poll query),
ad-audiences (the audience resource), custom-audiences,
remarketing-actions.
- Planning: keyword-planning (create only; action = ideas|
historical_metrics|forecast_metrics), geo-targets (query the
constants table; create = suggest by location name, no customer_id),
recommendations (query + create with REQUIRED action apply|dismiss|
generate — apply changes live spend).
- Account ops: customers (query own row; create = NEW client account
under an MCC; update = the account itself), customer-user-accesses,
customer-user-access-invitations, customer-client-links (manager
side), customer-manager-links (client side; action move),
customer-labels (label MCC children), product-links +
product-link-invitations (Merchant Center / GA4 / Hotel Center —
update ACCEPTED|REJECTED answers incoming invitations), and billing:
billing-setups, account-budget-proposals, invoices,
payments-accounts.
- Experiments: experiments (create shell; action schedule|promote|end|
graduate), experiment-arms.
- Bulk: batch-jobs (create -> action add_operations with mixed
mutateOperations -> action run -> poll query -> ?job= results).

Cross-cutting: money/bids are in MICROS (1,000,000 = 1 unit of account
currency). Pause/enable anything via update {status:"PAUSED"|"ENABLED"}.
validate_only:true dry-runs any mutate without applying it.
update_mask overrides the auto-derived mask (derived recursively from
the fields you send, so nested objects mask only their set leaf paths).
partial_failure is accepted where Google supports it.

Authentication. The connector OAuth establishes which endpointr customer
you are. This uses its OWN vault slot (google-ads) — separate from the
google slot used by Tasks/Search Console/Analytics, so consent for one
never implicitly grants the other. Store a refresh-triplet once via
PUT /v1/credentials/google-ads {client_id, client_secret, refresh_token}
and endpointr mints short-lived access_tokens on every call — or click
"Connect Google Ads" on the customer's admin page to run the consent flow
and have refresh_token written automatically (never type it by hand).
Alternatively pass oauth_token (a fresh access_token) or the
refresh-triplet inline per-request (credential fields are stripped before
reaching Google). Required scope: https://www.googleapis.com/auth/adwords.

Beyond OAuth, every call ALSO needs:
- developer_token (REQUIRED) — issued once per Ads MANAGER account via
the Ads UI (Tools & Settings > API Center); never rotates. Store it in
the google-ads vault entry, or pass developer_token per-request.
- login_customer_id (optional) — the manager account's id, needed only
when you reach the target account THROUGH a manager rather than
owning it directly. Same hybrid resolution.

When a token is minted from the vault, the new access_token is surfaced in
the response as refreshed_access_token so clients can cache it (~1h TTL).

Tool arguments mirror the HTTP request body / path id / query string.
create/update tools accept an open body object — the schema lists
documented fields, but bodies are forwarded to Google verbatim, so the
full v25 request shape of every resource is supported. Over MCP there is
no query string: customer_id rides in the query arg on query verbs,
in the body on create/update, and inside the composite/full-resource
id on delete.

claude mcp add googleads --transport http https://googleads.mcp.endpointr.com
MCPendpointr-tagmanager — https://tagmanager.mcp.endpointr.com
groups: tagmanager

This connector is a Google Tag Manager API (v2) relay exposed through
endpointr: full container management — accounts, containers, workspaces,
tags, triggers, variables, built-in variables, folders, environments, and
versioning INCLUDING publish-to-live. Each tool mirrors one REST endpoint
under /v1/tagmanager/*. Tool names follow:
endpointr_tagmanager_gtm_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (PUT), delete (DELETE).

THE ID MODEL (read this first): GTM addresses everything by a nested path
(accounts/{a}/containers/{c}/workspaces/{w}/tags/{t}). Collection verbs
(query/create) take the parent ids as account_id / container_id /
workspace_id (query arg on reads, body on writes). get/update/delete take
the target as id — either the full GTM path every resource carries, or
the colon composite of the same ids in order: a container is
accountId:containerId, a workspace accountId:containerId:workspaceId, a
tag/trigger/variable/folder appends its own id as the 4th part.

Typical flow: gtm-accounts list -> gtm-containers query ?account_id (the
numeric containerId is what the other tools take; publicId is the
GTM-XXXXXXX snippet id) -> gtm-workspaces query (find the Default
Workspace) -> create/edit tags, triggers and variables in that workspace ->
gtm-workspaces create {action:'create_version'} (snapshot) -> gtm-versions
create {action:'publish', version_id} (LIVE on the site).

Resources:
- gtm-accounts list = every GTM account the grant reaches (start
here); get by accountId. Read-only.
- gtm-containers query ?account_id = list; or look one up from its
public snippet id with ?destination_id=GTM-XXXXXXX (no
account needed). create needs {account_id, name,
usageContext:["web"]}. delete is IRREVERSIBLE.
- gtm-workspaces the editable draft layer every tag/trigger/variable
write happens in. create also folds the sub-methods:
{action:'create_version'} snapshots the workspace into
a version (response carries containerVersion.
containerVersionId), {action:'sync'} pulls
latest-version changes in.
- gtm-tags CRUD inside a workspace ({…ids, name, type,
parameter[], firingTriggerId[]}). Common types:
googtag (Google tag/GA4), gaawe (GA4 event), html
(custom HTML), awct (Ads conversion).
- gtm-triggers CRUD; types pageview, click, linkClick, customEvent,
scrollDepth, … A trigger's triggerId feeds tags'
firingTriggerId.
- gtm-variables user-defined variables CRUD; types v (data layer), c
(constant), jsm (custom JS), u (URL), … Reference as
{{Name}} from tag/trigger parameters.
- gtm-built-in-variables the one-click toggles (Page URL, Click
Classes, …): query = enabled ones, create = enable
({…ids, type:"pageUrl" | [types…]}), delete = disable
(id accountId:containerId:workspaceId:type).
- gtm-folders workspace organisation CRUD; file an entity by setting
parentFolderId on the entity itself.
- gtm-versions query ?account_id&container_id = version HEADERS;
get by a:c:versionId — or the specials a:c:live
(the published version) and a:c:latest. create is
action-only: {action:'publish'|'undelete'|'set_latest',
account_id, container_id, version_id} — PUBLISH PUSHES
LIVE. update renames; delete trashes a version.
- gtm-environments live/latest plus custom preview/staging environments
(auth codes + preview links). Full CRUD.

Cross-cutting: every response wraps the GTM payload as {data: …}. Mutable
resources carry a fingerprint — pass it on update/publish for
optimistic-concurrency protection (omitted = last write wins). Writes land
in a workspace and are NOT live until a version is created and published.

MCP note: there is no query string over MCP, so parent ids ride the query
arg on query verbs, the body on create/update, and the composite/full-path
id on get/delete.

Authentication. The connector OAuth establishes which endpointr customer
you are. This uses its OWN vault slot (google-tagmanager) — separate from
the google slot used by Tasks/Search Console/Analytics, so Tag Manager's
write+publish consent is granted independently. Store a refresh-triplet
once via PUT /v1/credentials/google-tagmanager {client_id, client_secret,
refresh_token}
and endpointr mints short-lived access_tokens on every call
— or click "Connect Google Tag Manager" on the customer's admin page to run
the consent flow and have refresh_token written automatically. Alternatively
pass oauth_token (a fresh access_token) or the refresh-triplet inline
per-request (credential fields are stripped before reaching Google).
Scopes: tagmanager.readonly, tagmanager.edit.containers,
tagmanager.edit.containerversions, tagmanager.publish,
tagmanager.delete.containers.

When a token is minted from the vault, the new access_token is surfaced in
the response as refreshed_access_token so clients can cache it (~1h TTL).

Tool arguments mirror the HTTP request body / path id / query string.
create/update tools accept an open body object — the schema lists
documented fields, but bodies are forwarded to Google verbatim, so the full
v2 request shape of every resource is supported.

claude mcp add tagmanager --transport http https://tagmanager.mcp.endpointr.com
MCPendpointr-reddit-ads — https://redditads.mcp.endpointr.com
groups: redditads

This connector is a Reddit Ads API (v3, https://ads-api.reddit.com/api/v3)
relay exposed through endpointr: full campaign management, audiences,
creatives, conversions (CAPI), product catalogs, targeting reference data,
and reporting. Each tool mirrors one or more REST endpoints under
/v1/redditads/*. Tool names follow:
endpointr_redditads_reddit_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (PATCH upstream), delete (DELETE).

Id chain (chain these): reddit-me list (smoke test) -> reddit-businesses
list (business_id) -> reddit-ad-accounts query ?business_id (ad_account_id)
-> campaigns / ad-groups / ads / custom-audiences / saved-audiences /
lead-gen-forms / reports, all keyed by ad_account_id. Posts, structured
posts and creative assets hang off reddit-profiles (profile_id, from
?ad_account_id or ?business_id). Product feeds/sets/products hang off
reddit-product-catalogs (catalog_id, from ?business_id). Campaign create
also needs a funding_instrument_id (reddit-funding-instruments).

Campaign build order: campaign create (objective, funding_instrument_id)
-> ad-group create (campaign_id + targeting/bid; validate targeting values
with reddit-targeting first) -> post create (profile_id) -> ad create
(ad_group_id + post_id). Start everything configured_status=PAUSED and
flip to ACTIVE when ready.

Reddit API shapes the relay smooths over:
- Write bodies are auto-wrapped in Reddit's {"data": {...}} envelope —
pass flat fields (an explicit data key is forwarded as-is).
- Updates are PATCH upstream; send only the fields to change. There is
NO delete for campaigns/ad groups/ads — archive/pause via
update {configured_status: PAUSED|ARCHIVED}.
- Lists paginate via page.size/page.token (aliases page_size/
page_token accepted) and return pagination.next_url — pass that URL
back as a next_url query field to fetch the next page.
- Money is in MICROCURRENCY (1,000,000 = 1 unit of the account
currency); timestamps are ISO 8601 UTC.
- MCP note: parent ids ride the query arg on query verbs and the
body on create; get/update/delete take only the object id — Reddit
ids are globally unique, so no composite ids anywhere.

Authentication. The connector OAuth establishes which endpointr customer
you are. This uses its OWN vault slot (reddit-ads). Store client_id +
client_secret once and run the admin page's "Connect Reddit Ads" consent
flow to mint the refresh_token automatically — or PUT the full triplet via
PUT /v1/credentials/reddit-ads {client_id, client_secret, refresh_token}.
endpointr exchanges the triplet for a short-lived (~24h) access_token on
every call and persists any rotated refresh_token. Alternatively pass
oauth_token (a fresh access_token) or the refresh-triplet inline
per-request (credential fields are stripped before reaching Reddit). An
optional vault user_agent overrides the descriptive User-Agent endpointr
sends (Reddit blocks generic UAs). Scopes: adsread adsedit adsconversions
adsdatadeletion. Reads work for any authorized app; CREATE/EDIT endpoints
additionally require Reddit's Ads API developer approval.

When a token is minted from the vault, the new access_token is surfaced in
the response as refreshed_access_token so clients can cache it (~24h TTL).

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists
documented fields, but handlers forward additional keys to Reddit verbatim.

claude mcp add redditads --transport http https://redditads.mcp.endpointr.com
MCPendpointr-sendmails — https://sendmails.outreach.mcp.endpointr.com
groups: sendmails

This connector is a SendMails.io relay (a hosted Acelle Mail —
https://app.sendmails.io/api/v1) exposed through endpointr: newsletter
campaigns, mail lists, and subscribers. Each tool mirrors one REST endpoint
under /v1/sendmails/*. Tool names follow:
endpointr_sendmails_sendmails_<resource>_<verb>
where verb is one of: list (GET collection, no params), query (GET with
params), get (GET by uid), create (POST), update (PATCH upstream), delete.

Typical newsletter flow: lists list (pick/create a list_uid) -> subscribers
create (add recipients) -> campaigns create (the newsletter: subject, html,
from) -> campaigns create {action:'run', uid} (start sending) -> campaigns
get (delivery statistics).

Resources:
- lists list = all mail lists, NO parameters — the id source: each
result's uid is the list_uid subscribers + campaigns
need. get by uid. create needs name, from_email, from_name
+ a contact block (company, address_1, city, zip, country_id,
email, phone?). Fold {action:'add_field', uid, type, label,
tag} to add a custom subscriber field. delete by uid.
- subscribers FULL CRUD (create/update/delete all write — not read-only).
query = subscribers on a list (REQUIRES list_uid; filters:
per_page, page, keyword?). get by uid. create adds one
({list_uid, EMAIL, FIRST_NAME?, LAST_NAME?, ...}). Fields are
keyed by their Acelle identifier (the uppercase field key:
EMAIL, FIRST_NAME, custom fields). Label a subscriber with the
built-in tag field — SINGULAR, a comma-separated STRING
(e.g. "vip,newsletter"); there is no tags array, and sending
tags is silently ignored. status field = subscribed|
unsubscribed. Fold {action:'subscribe'|'unsubscribe', uid} to
flip status. update by uid (PATCH, send only changed fields);
delete by uid.
- campaigns THE NEWSLETTER SURFACE. list = all campaigns, NO parameters
(each result's uid is the campaign id). get by uid returns
details + statistics (sent/open/click/bounce). create a
draft: {name, list_uid, subject, from_email, from_name,
reply_to, html, plain?, track_open?/track_click? ('yes'/'no'),
run_at? ('Y-m-d H:i:s' to schedule)}. Fold
{action:'run'|'pause'|'resume', uid} for the send lifecycle.
update a draft by uid; delete by uid.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream api_token is resolved per customer from the vault — store
it once via PUT /v1/credentials/sendmails {api_token:"…"} (from the
SendMails.io dashboard, Account > API). Vault-only.

Tool arguments mirror the HTTP request body / path id / query string; extra
fields are forwarded to SendMails verbatim.

claude mcp add sendmails --transport http https://sendmails.outreach.mcp.endpointr.com
MCPendpointr-encharge — https://encharge.outreach.mcp.endpointr.com
groups: encharge

This connector is an Encharge.io relay (https://api.encharge.io/v1) exposed
through endpointr: people, tags, fields, segments, webhooks, account. Each
tool mirrors one REST endpoint under /v1/encharge/*. Tool names follow:
endpointr_encharge_encharge_<resource>_<verb>
where verb is one of: list (GET, no params), query (GET with params),
create (POST), update (PATCH upstream), delete (DELETE).

THE OUTREACH MODEL — read this first: Encharge's public API has NO
campaign/newsletter endpoints. Emails are sent by Flows built in the
Encharge UI; the API's job is getting people INTO those flows. To send
outreach: people create (upsert the person) -> tags create ({tag:"…",
email}) — a Flow triggered by that tag sends the email sequence.

Resources:
- account list = account info (peopleCount, timezone, …), NO parameters.
The connector smoke test — call it first.
- people query = fetch specific people by identifier (email / user_id /
id; emails comma-list for several; raw people array for
mixed identifiers). create = UPSERT one person object or a
people array (identified by email/userId/id; any extra keys
become field values). Fold {action:'unsubscribe', email} to
stop all email to a person. delete by id (the person's email
or Encharge id; add force=true for GDPR wipe — else archives).
- tags create = add tag(s) ({tag:"a,b", email}) — THE outreach
trigger. Fold {action:'remove', tag, email} to untag. Tags are
free strings; no upstream list — a person's current tags are on
their tags field via people query.
- fields list = all person fields, NO parameters (the field name is
the id). create ({name, type, title?} or a fields array),
update by fieldName, delete by fieldName.
- segments list = all segments, NO parameters (id source). query = people
in one segment (REQUIRES segment_id; limit/offset/attributes/
sort/order).
- webhooks create = subscribe to Encharge events ({eventType, targetUrl};
eventTypes: newUser, updatedUser, unsubscribedUser,
added-tag-<TAG>, removed-tag-<TAG>). delete by the numeric id
FROM THE CREATE RESPONSE — no upstream list (keep the id).

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream API key is resolved per customer from the vault — store it
once via PUT /v1/credentials/encharge {api_key:"…"} (Encharge > Settings >
Account). Sent as the X-Encharge-Token header. Vault-only.

Tool arguments mirror the HTTP request body / path id / query string; extra
person keys are forwarded to Encharge verbatim (they become field values).

claude mcp add encharge --transport http https://encharge.outreach.mcp.endpointr.com
MCPendpointr-sendr — https://sendr.outreach.mcp.endpointr.com
groups: sendr

This connector is a Sendr.io relay (https://api.sendr.io) exposed through
endpointr: signal-driven outreach — contact sheets feeding campaign
sequences, personalized pages (GIF / dynamic audio / lip-sync video), and a
unified reply inbox. Each tool mirrors one REST endpoint under /v1/sendr/*.
Tool names follow:
endpointr_sendr_sendr_<resource>_<verb>
where verb is one of: list (GET, no params), query (GET with params),
get (GET by id), create (POST), update (PATCH upstream), delete.

THE MODEL: a campaign runs on a SHEET of contacts. Campaigns are built in
the Sendr app and are READ-ONLY via the API — you add contacts by adding
ROWS to the campaign's sheet. Replies land in the INBOX. Personalized
pages are generated from PAGE TEMPLATES.

Resources & id chains:
- seat list = API-user/workspace info, NO parameters. The
connector smoke test — call it first. Also the seat id
source for inbox assignment.
- sheets list (no params) / query (offset, limit, name,
campaignId) / get. Each sheet's id feeds sheet-columns
and sheet-rows.
- sheet-columns query REQUIRES sheet_id — the sheet's field schema;
column names are the keys sheet-rows accepts.
- sheet-rows create REQUIRES sheet_id + the row fields (column
names) — HOW YOU ADD A CONTACT to a campaign.
- campaigns list (no params) / query (page, limit, search, status
DRAFT|ACTIVE|PAUSED) / get. READ-ONLY upstream — no
create/pause via API. Each campaign carries its
sheetId (feed sheet-rows to add contacts).
- page-templates list = templates (no params; the templateId source);
get = a template's VARIABLE TAGS (the keys for
variablesValues).
- pages create = generate a personalized page ({templateId,
variablesValues, gifSource?, videoBackgroundUrl?,
attributes?, webhookUrl?}) -> {pageId, pageUrl}. get by
pageId/slug = status (pending|done|failed) + generated
assets. Poll get until done, or use webhooks.
- dynamic-audio create = queue personalized audio ({audioUrl,
targetWord, replacementWord}) -> jobId.
- video create = queue personalized video (same + videoUrl;
mode merge|lipsync|video_only) -> jobId.
- webhooks list = workspace webhooks (no params). Sendr keys
webhooks BY URL, so mutations fold onto create:
create {name, url, events?} | {action:'update', url, …}
| {action:'delete', url} | {action:'toggle', url,
enabled} | {action:'reveal_secret', url}. Events:
page:pending/done/failed, engagement:page_view/
audio_play/video_play/button_click/meeting_booked, ….
- inbox list = 25 latest reply threads (no params) / query
(search, status unread|deleted, channels, campaign_ids,
tag_ids, starred, cursor, limit) / get by threadId.
create = act via action: send {thread_id, body} (reply),
read/unread, star/unstar, tag/untag {thread_id, tag_id},
delete/restore {thread_ids[]}, assign {thread_ids[],
seat_id}.
- inbox-messages query REQUIRES thread_id (+cursor/limit) = the thread's
messages; add message_id = attachment list; add
attachment_id = download (base64 + mime).
- inbox-tags list/query/create/update/delete — the tag_id source.
- inbox-campaigns list = campaigns that have inbox threads (the
campaign_ids filter source).
- analyze-website create {url} = about text + generated buyer personas.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream API key is resolved per customer from the vault — store it
once via PUT /v1/credentials/sendr {api_key:"…"} (generate it at
app.sendr.io > Settings > API keys). Sent as the X-API-Key header. Vault-only.

Tool arguments mirror the HTTP request body / path id / query string; extra
fields are forwarded to Sendr verbatim.

claude mcp add sendr --transport http https://sendr.outreach.mcp.endpointr.com
MCPendpointr-leadshark — https://leadshark.mcp.endpointr.com
classes: Leadshark*

This connector is a LeadShark relay (https://apex.leadshark.io) exposed through
endpointr: LinkedIn enrichment, post-engagement AUTOMATIONS, lead capture,
scheduled posts, lead-magnet pages/links, engagement signals, and webhooks.
Each tool mirrors one REST endpoint under /v1/social/leadshark-*. Tool names:
endpointr_social_leadshark_<resource>_<verb>
where verb is one of: list (GET, no params), query (GET with params),
get (GET by id), create (POST), update (PUT), delete (DELETE).

THE AUTOMATION MODEL (the headline feature): an automation watches ONE
LinkedIn post and DMs/replies/connects with people who comment matching
keywords. To create one you need the post's URN + share URL, which come from
the posts tool:
1. leadshark-posts query -> pick a post; copy item.post_id (a URN like
urn:li:activity:7150…) and item.share_url.
2. leadshark-automations create {name, post_id:<URN>, linkedin_post_url:
<share_url>, keywords, dm_template, …}.
Use the FULL URN exactly — the numeric tail alone creates the row but
engagement won't bind. Pass only {name, post_id, linkedin_post_url} for a
webhook-only automation (comments stream to your webhooks; no LeadShark-side
actions). Toggle Running/Paused/Stopped via leadshark-automation-status.

Resources & id chains:
- enrich-person / enrich-company : query = enrich a LinkedIn profile/company
(real profile view; soft cap ~200-250/day).
- linkedin-search : create = LinkedIn people/company search.
- posts : query = your (or anyone's, via linkedin_id) LinkedIn
posts. THE id source for automations (post_id + share_url)
and the engagement tools (post_id URN).
- automations : list/query/get/create/update/delete. create binds to a
post (see above). automation-status update flips status.
automation-templates query = reusable templates; each id
is a template_id for scheduled-posts.
- leads : query = captured leads (email + ICP score when present).
- bookmarks / bookmark-tags : save LinkedIn profiles with tags/notes.
- scheduled-posts : list/create/update/delete. create {content,
scheduled_time} (15 min-90 days out) with an optional
automation pre-automation that activates on publish —
it accepts page_id (links_enabled:true) or template_id.
- post-stats : query = post metrics; summary=true = lifetime totals
(Apex). dashboard-activity query = activity rollups.
- discover (Apex): fresh lead-magnet posts to engage.
- signals (Apex) : signals = ranked hot leads; signal-events = raw events
(type + since/until or since_captured/until_captured).
- engagement (Pro+): post-reactions / post-comments / post-reposts REQUIRE
post_id (URN from posts). profile-viewers = Apex + Premium.
- links (Pro+) : list/get/create/delete tracking links; each slug feeds
link-analytics and link-events. Attach a page via page_id.
- pages (Pro+) : quiz lead-magnet pages CRUD; each id is a page_id for
links and pre-automations; page-stats / page-responses /
page-emails REQUIRE page_id.
- webhooks (Pro) : CRUD + webhook-test. event_types new_comment /
email_captured / lead_sent (Pro) + new_profile_visit /
new_like (Apex only). HMAC-SHA256 signed; secret shown once.

Tier gating (Pro / Pro+ / Apex) is enforced by LeadShark — a call outside your
plan returns 403 with an explanatory body. Rate limits: 250/hr, 1000/day,
100/min (429 on exceed — back off).

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream key is resolved per customer from the vault — store it once
via PUT /v1/credentials/leadshark {api_key:"…"} (LeadShark Settings → API
Access). Sent as the x-api-key header. You may also pass api_key inline in
any tool's body/query for per-request passthrough; it is stripped before the
request reaches LeadShark.

claude mcp add leadshark --transport http https://leadshark.mcp.endpointr.com
MCPendpointr-paypal — https://paypal.mcp.endpointr.com
classes: Paypal*

This connector is a PayPal REST API relay (https://api-m.paypal.com) exposed
through endpointr, scoped to READ-ONLY money reporting — invoices,
subscriptions, balance, and income. No write/refund/charge operations. Each
tool mirrors one PayPal endpoint. Tool names follow:
endpointr_payments_paypal_<resource>_<verb>
where verb is: list (GET collection / singleton), query (GET/search with
params), get (GET by id).

Resources:
- overview list = a computed snapshot in ONE call: balances +
invoices bucketed PAID / OUTSTANDING / OVERDUE + gross
income over the last 30 days, grouped per currency. Start
here for "how are we doing".
- invoices query = list invoices, or SEARCH when you pass filters
(status e.g. PAID|SENT|UNPAID|PAYMENT_PENDING|
MARKED_AS_PAID|PARTIALLY_PAID|CANCELLED, invoice_date_range,
due_date_range, recipient_email, total_amount_range,
plus page/page_size). get = one invoice by id. PayPal has
no "overdue" status — overdue = outstanding past its due date
(filter due_date_range, or read overview.invoices.overdue).
- transactions query = Transaction Search — the true "overall income"
source (every payment received, not just invoiced).
REQUIRES start_date+end_date (RFC3339), max 31 days apart;
page by month for longer ranges.
- balance list = current available money on the account, per currency.
- plans query = billing plans (the NAMED recurring products). This
is how you "search subscriptions by name/description" — pass
name/description (filtered client-side within the fetched
page). get = one plan by id (P-…).
- subscriptions get = one subscription by id (I-…): status, plan_id,
subscriber, next billing, last payment, outstanding balance.
PayPal has NO list/search for subscriptions — find the plan
by name via plans, read a subscriber's sub by its I-… id.
- accounts list = the configured PayPal account labels (see below).

balance, transactions, and overview's balance/income need the PayPal REST
app to have the "Transaction Search" feature enabled; data can lag live by ~3h.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream PayPal credential is resolved per customer from the vault —
store it once via PUT /v1/credentials/paypal
{client_id, client_secret, mode}
(mode = live|sandbox; from a REST API app at
developer.paypal.com). The relay exchanges it for a short-lived access_token
(client-credentials) and caches it — you never handle tokens. Vault-only:
client_id/client_secret are never accepted per-request.

Multiple PayPal accounts. One login can hold several accounts, each stored
under paypal:<label> (e.g. paypal:conzent). EVERY tool accepts an optional
top-level account argument naming which to target; when more than one is
configured it is REQUIRED (a call that omits it is rejected with the list of
labels). Call the accounts-list tool to see them. A single unlabelled paypal
credential lets you omit account.

Money values are decimal major units with a currency code. Tool arguments
mirror the HTTP query string / path id.

claude mcp add paypal --transport http https://paypal.mcp.endpointr.com
MCPendpointr-stripe — https://stripe.mcp.endpointr.com
classes: Stripe*

This connector is a Stripe API relay (https://api.stripe.com/v1) exposed
through endpointr, scoped to the core payments resources. Each tool mirrors one
Stripe REST endpoint. Tool names follow:
endpointr_payments_stripe_<resource>_<verb>
where verb is one of: list (GET collection), query (GET with query params),
get (GET by id), create (POST), update (POST upstream), delete (DELETE).

Resources: payment-intents, charges, customers, payment-methods, products,
prices, subscriptions, invoices, refunds, payouts, balance, balance-transactions,
checkout-sessions, setup-intents, events, overview, accounts.

The overview resource is a computed business-health snapshot (MRR, ARR, active
subscriptions & customers, new subs and cancellations over 1/7/30 days, refunds,
monthly churn, ARPU, LTV) grouped per currency — call
endpointr_payments_stripe_overview_list.

Multiple Stripe accounts. One login can hold several Stripe accounts (e.g. two
companies), each stored under its own credential stripe:<label> via
PUT /v1/credentials/stripe:<label> {api_key:'sk_live_…'} (e.g. stripe:rexultz,
stripe:conzent). EVERY tool accepts an optional top-level account argument
naming which account to target, e.g.
endpointr_payments_stripe_overview_list {account:'rexultz'} or
endpointr_payments_stripe_subscriptions_query {account:'conzent', query:{status:'active'}}.
When more than one account is configured, account is REQUIRED — a call that
omits it is rejected with the list of valid labels. Call
endpointr_payments_stripe_accounts_list to see the configured labels. If only a
single unlabelled stripe credential exists, account may be omitted.

Stripe uses POST for both create and update, and it has non-CRUD verbs
(confirm, capture, cancel, finalize, pay, send, void, attach, detach, reverse,
expire). Those are folded onto the create tool via an action field plus the
target id — e.g. confirm a PaymentIntent with
create {action:'confirm', id:'pi_…', payment_method:'pm_…'}, finalize an
invoice with create {action:'finalize', id:'in_…'}, attach a PaymentMethod
with create {action:'attach', id:'pm_…', customer:'cus_…'}. Without an
action, create POSTs to the bare collection to create a new resource. Each
tool's description lists its available actions.

Amounts are in the currency's smallest unit (e.g. 1099 = $10.99; zero-decimal
currencies like JPY use the whole number). Nested fields use Stripe's object
shape in the body/query args (e.g. {metadata:{order_id:'…'}},
{items:[{price:'price_…'}]}) — the relay serialises them to Stripe's
bracket form.

Authentication. The connector OAuth establishes which endpointr customer you
are. The upstream Stripe secret key is resolved per customer from the credential
vault — store it once via PUT /v1/credentials/stripe {api_key:'sk_live_…'}
(test mode: sk_test_…). Vault-only: the secret key is never accepted
per-request.

Tool arguments mirror the HTTP request body / path id / query string. Most
create/update tools accept an open body object — the schema lists documented
fields, but handlers forward additional keys to Stripe verbatim.

claude mcp add stripe --transport http https://stripe.mcp.endpointr.com
MCPendpointr-easifynow — https://easifynow.mcp.endpointr.com
groups: linkbuilding · classes: Easifynow*

This connector is an Easifynow relay (https://api.easifynow.com) exposed through
endpointr. Easifynow is a linkbuilding platform: a catalogue of publisher
domains and an endpoint that publishes an article with links on one of them.
Each tool mirrors one route under /v1/linkbuilding/easifynow-*. Tool names:
endpointr_linkbuilding_easifynow_<resource>_<verb>
where verb is one of: list (GET, no params), query (GET with params),
get (GET by id), create (POST), delete (DELETE).

PUBLISHING IS PUBLIC AND COSTS MONEY. articles create without dry_run puts an
article live on a third-party website and spends one credit. Show the user the
domain, title, links and anchor texts and get a clear yes before calling it.
Always run it with dry_run:true first.

Typical flow for one client site:
1. account list credits left (and proves the login works)
2. pick query {target} the best domains for the client site, each with
its numbers and a reason. Domains that already
link to the target are excluded.
3. domain-categories get the chosen domain's WordPress categories; one
wp_category_id becomes selected_category_id
4. Write the article in the domain's language (a .dk domain needs Danish):
at least 500 words, at most 3 external links each with anchor text, no
script/iframe/hidden text. Mostly branded or natural anchors.
5. articles create {..., dry_run:true} local + Easifynow validation,
nothing published, no credit spent
6. articles create {...} publish, after the user approves
7. articles get <submission_id> app_status, and wp_post_url once live

Resources:
- account list = credits, submission stats, link statistics.
- tlds list = country TLDs the account can publish on.
- domains list = first 200 domains by traffic; query = the
catalogue with filters (tld, id_category,
exclude_linking_to, sort_by trafik|dr|ref_domains, ...).
trafik is monthly traffic, navn the site name.
- pick query = ranked domains for a target site.
- domain-categories get = a domain's categories (by domain id).
- articles list/query = submission history; get = one submission;
create = publish (or dry_run, or action:'update' to
change a live article); delete = trash a live article.

The featured image is required: pass image_url (public https, at least
800x400 px). The relay fetches it and brings it under Easifynow's 1 MB cap.

Authentication. The connector OAuth establishes which endpointr customer you
are. Easifynow signs in with email + password (no API key); store them once via
PUT /v1/credentials/easifynow {email, password}. The relay logs in, caches the
token and renews it, so you never handle tokens.

claude mcp add easifynow --transport http https://easifynow.mcp.endpointr.com

Auth

POST/v1/token
no auth this endpoint does not require a Bearer token.

Issue a JWT from a valid api_key.

Body:
{ "api_key": "…" }

On success, this request auto-captures the JWT into the collection variable token, so every subsequent request gets Authorization: Bearer {{token}} automatically.

Content-Typeapplication/json
{
    "api_key": "YOUR_API_KEY"
}
curl -X POST 'https://api.endpointr.com/v1/token' \
  -H 'Content-Type: application/json' \
  -d '{
    "api_key": "YOUR_API_KEY"
}'
const response = await fetch('https://api.endpointr.com/v1/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
      "api_key": "YOUR_API_KEY"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"api_key\": \"YOUR_API_KEY\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/token

Introspect the current token without erroring on edge cases.

Returns {valid, reason, jti, expires_in, expired, revoked, user}. reason is one of:
- null — token is good
- missing — no Bearer header
- expired — past exp
- revoked — jti in revoked_tokens (another device called DELETE)
- bad_signature / malformed — not our token

Use this as a pre-flight: if expires_in < 60, call PATCH to rotate; if revoked, re-exchange your api_key with POST.

Tip: You can usually skip this — the JWT is self-describing. Base64-decode the middle segment client-side and read exp directly. The one thing only this endpoint can tell you is *revocation*.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/token' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/token', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PATCH/v1/token

Rotate the current token. Revokes the presented jti and issues a fresh one.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X PATCH 'https://api.endpointr.com/v1/token' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/token', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/token

Revoke the current token (logout).

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/token' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/token', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/keys

Upload your PEM public key (≥ 2048 bits).

Private keys MUST NOT be sent.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "publicKey": "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----"
}
curl -X POST 'https://api.endpointr.com/v1/keys' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "publicKey": "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----"
}'
const response = await fetch('https://api.endpointr.com/v1/keys', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "publicKey": "-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/keys');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"publicKey\": \"-----BEGIN PUBLIC KEY-----\\n…\\n-----END PUBLIC KEY-----\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/keys

Return the stored public key for the authenticated customer.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/keys' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/keys', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/keys');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Credentials

GET/v1/credentials/:service

Check whether creds exist for a service (does not return values).

:service — e.g. openai, anthropic, openrouter, claude-cli, openai-cli, stripe, mailchimp, ipregistry …

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/credentials/:service' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/credentials/:service', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/credentials/:service');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/credentials/:service

Store the JSON credential map for a service. Body is the map itself.

AI providers — auto-appended to the customer's provider-priority list on save (admin can reorder via /admin/customers/{id} → Provider priority):
- openai: { "api_key": "sk-…", "organization": "org-…" } (organization optional)
- anthropic: { "api_key": "sk-ant-…" }
- openrouter: { "api_key": "sk-or-…", "referer": "https://yourapp.com", "app_title": "YourApp" } (referer + app_title optional, used for OpenRouter's app rankings)
- claude-cli: { "oauth_token": "sk-ant-oat01-…" } (from claude setup-token)
- openai-cli: { "api_key": "sk-…" }

Other services:
- stripe: { "api_key": "sk_live_…", "webhook_secret": "whsec_…" } (webhook_secret optional)
- mailchimp: { "api_key": "…-usX", "list_id": "abc" } (list_id optional)
- ipregistry: { "api_key": "…" }

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "api_key": "REPLACE_ME"
}
curl -X PUT 'https://api.endpointr.com/v1/credentials/:service' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "api_key": "REPLACE_ME"
}'
const response = await fetch('https://api.endpointr.com/v1/credentials/:service', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "api_key": "REPLACE_ME"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/credentials/:service');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"api_key\": \"REPLACE_ME\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/credentials/:service

Remove stored creds for a service. If the service is an AI provider in the priority list, it is also removed from priority.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/credentials/:service' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/credentials/:service', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/credentials/:service');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Vault

GET/v1/vault

List the caller's vault entries. Returns names and updated_at only — values stay sealed. Use GET /v1/vault/:name to fetch one.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/vault' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/vault', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/vault');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/vault/:name

Fetch the decrypted JSON object stored at :name. Unlike /v1/credentials/:service, this returns the actual values — that's the point of the vault.

Use case. Your SaaS calls this at runtime to retrieve secrets (Amazon SES SMTP, ShortPixel keys, etc.) instead of hardcoding them. Rotate by editing the vault entry once in /admin/customers/{id} → Vault; every app picks up the new value on its next fetch.

Naming. :name is customer-chosen, lowercase: [a-z0-9][a-z0-9_-]{0,63}. Pick something descriptive (e.g. ses-prod, shortpixel, mailgun-eu).

Response: { "name": "ses-prod", "data": { …whatever you stored… } }. 404 if the name is unknown for this customer.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/vault/:name' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/vault/:name', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/vault/:name');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/vault/:name

Create or replace the vault entry at :name. Body is the JSON object to store — any shape you like, since you'll be reading it back yourself.

Example — Amazon SES SMTP:

{
  "smtp_host": "email-smtp.us-east-1.amazonaws.com",
  "smtp_port": 587,
  "smtp_user": "AKIA…",
  "smtp_pass": "BJ…"
}

Example — ShortPixel:

{ "api_key": "…" }

Existing entries are overwritten in place. Encrypted at rest with the same XSalsa20-Poly1305 key as /v1/credentials/:service.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "smtp_host": "email-smtp.us-east-1.amazonaws.com",
    "smtp_port": 587,
    "smtp_user": "AKIA…",
    "smtp_pass": "REPLACE_ME"
}
curl -X PUT 'https://api.endpointr.com/v1/vault/:name' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "smtp_host": "email-smtp.us-east-1.amazonaws.com",
    "smtp_port": 587,
    "smtp_user": "AKIA…",
    "smtp_pass": "REPLACE_ME"
}'
const response = await fetch('https://api.endpointr.com/v1/vault/:name', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "smtp_host": "email-smtp.us-east-1.amazonaws.com",
      "smtp_port": 587,
      "smtp_user": "AKIA…",
      "smtp_pass": "REPLACE_ME"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/vault/:name');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"smtp_host\": \"email-smtp.us-east-1.amazonaws.com\",\n    \"smtp_port\": 587,\n    \"smtp_user\": \"AKIA…\",\n    \"smtp_pass\": \"REPLACE_ME\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/vault/:name

Remove the vault entry at :name.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/vault/:name' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/vault/:name', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/vault/:name');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

AI

Chat

POST/v1/ai/chat

Text chat across providers. Canonical body is the minimum that works when you've stored credentials for at least one provider via PUT /v1/credentials/:service — Endpointr walks the customer's priority list, picks the first survivor, and falls over to the next on 5xx / 401 / 403 / 429.

Provider selection. provider is optional. Omit it to use the priority list (set under /admin/customers/{id} → Provider priority). Pass it explicitly to pin one provider with no failover. Valid values: openai, anthropic, openrouter, claude-cli, openai-cli.

Credentials. Resolution order:
1. Per-request override in the body — oauth_token (for claude-cli) or api_key (for openai-cli). API providers have no body override; use stored creds.
2. Stored cred via PUT /v1/credentials/:service. Schemas:
- openai → {api_key, organization?}
- anthropic → {api_key}
- openrouter → {api_key, referer?, app_title?}
- claude-cli → {oauth_token}
- openai-cli → {api_key}
3. Server-wide env (CLAUDE_CODE_OAUTH_TOKEN, codex login state) — last resort.

Body shape. Send either prompt (string) or messages: [{role, content}, ...]. system is appended to the message list as a system role. json: true forces JSON output (provider permitting).

Sampling controls (temperature, max_tokens, top_p, stop) work for openai, anthropic, openrouter. The CLI providers ignore them — the underlying binaries don't expose those flags. If you need sampling control, pin provider: openai or provider: anthropic.

Other example bodies.

Explicit OpenAI with sampling controls:

{"provider":"openai","model":"gpt-4o-mini","temperature":0.2,"max_tokens":150,"prompt":"Summarize the goal of OAuth2 in one sentence."}

Explicit Anthropic via API key (set anthropic.api_key once via /v1/credentials/anthropic; no body creds needed):

{"provider":"anthropic","model":"claude-sonnet-4-6","prompt":"Why is HTTPS preferred over HTTP?"}

Claude-CLI subscription with body-supplied token:

{"provider":"claude-cli","model":"claude-sonnet-4-6","oauth_token":"{{claude_oauth_token}}","system":"You are concise.","prompt":"Write one sentence about rain."}

Messages array with multi-turn history:

{"messages":[{"role":"system","content":"You are concise."},{"role":"user","content":"What's 2+2?"},{"role":"assistant","content":"4."},{"role":"user","content":"And times 3?"}]}

Force JSON output:

{"prompt":"Return {\"colors\": [...]} with three primary colors.","json":true}

Minimal body: {"prompt":"Write one sentence about rain."}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "Write one sentence about rain."
}
curl -X POST 'https://api.endpointr.com/v1/ai/chat' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "Write one sentence about rain."
}'
const response = await fetch('https://api.endpointr.com/v1/ai/chat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "Write one sentence about rain."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/chat');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"Write one sentence about rain.\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Chat (SSE stream)

POST/v1/ai/stream

Same shape as /v1/ai/chat, but proxies the provider's native SSE stream back to you. Returns text/event-stream.

No failover for streaming — once SSE headers are out, you can't switch providers mid-flight. The first qualifying candidate handles the stream; mid-stream errors surface as SSE error frames.

Supported: claude-cli, anthropic, openai, openrouter. openai-cli does not support streaming (501).

Tip. Postman shows the full response after it completes; use curl -N in a terminal to see chunks live.

Other example bodies.

Explicit OpenAI:

{"provider":"openai","model":"gpt-4o-mini","max_tokens":200,"prompt":"Write a haiku about rain."}

Claude-CLI with body-supplied token:

{"provider":"claude-cli","model":"claude-sonnet-4-6","oauth_token":"{{claude_oauth_token}}","prompt":"Write a haiku about rain.","max_tokens":200}

Minimal body: {"prompt":"Write a haiku about rain.","max_tokens":200}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "Write a haiku about rain.",
    "max_tokens": 200
}
curl -X POST 'https://api.endpointr.com/v1/ai/stream' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "Write a haiku about rain.",
    "max_tokens": 200
}'
const response = await fetch('https://api.endpointr.com/v1/ai/stream', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "Write a haiku about rain.",
      "max_tokens": 200
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/stream');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"Write a haiku about rain.\",\n    \"max_tokens\": 200\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Conversation (SSE stream)

POST/v1/ai/conversation-stream

Streaming twin of POST /v1/ai/conversation. Same per-customer memory + Elasticsearch retrieval pipeline and narration-style system prompt — but the assistant reply is delivered as Server-Sent Events for low-latency voice / UI use cases.

Wire format (Content-Type: text/event-stream):
1. event: endpointr_retrieval — retrieval status + hits, emitted before the model starts.
2. Provider-native SSE frames — Anthropic content_block_delta, OpenAI choices[].delta.content, or ClaudeCli stream-json.
3. event: endpointr_done — final conversation id + usage metadata after the stream ends.

Memory is loaded before any output, so memory failures surface as a normal JSON 4xx/5xx. Once the retrieval frame is on the wire, errors become SSE error frames rather than HTTP errors.

Body fields are identical to /v1/ai/conversation (prompt OR messages, optional provider/model/temperature/max_tokens/stop/oauth_token/api_key/system/retrieval/memory). stream is implied — stream: false is rejected (use the non-streaming endpoint instead). json: true is rejected (incompatible with conversational narration output).

Tip. Postman buffers SSE bodies — you'll see the full stream after the request ends. Use curl -N to watch frames live.

Minimal body: {"prompt":"How do I refund a charge?","retrieval":{"k":4}}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "How do I refund a charge?",
    "retrieval": {
        "k": 4
    }
}
curl -X POST 'https://api.endpointr.com/v1/ai/conversation-stream' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "How do I refund a charge?",
    "retrieval": {
        "k": 4
    }
}'
const response = await fetch('https://api.endpointr.com/v1/ai/conversation-stream', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "How do I refund a charge?",
      "retrieval": {
          "k": 4
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/conversation-stream');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"How do I refund a charge?\",\n    \"retrieval\": {\n        \"k\": 4\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Conversation (with memory + retrieval)

POST/v1/ai/conversation

Conversational chat with server-side memory and per-customer Elasticsearch retrieval, tuned for narration / TTS / voice agents.

Unlike /v1/ai/chat, the response is plain spoken prose — no markdown, no bullet points, no headers. Think "two friends talking," not a chatbot dumping a formatted answer.

Memory scope. One thread per customer (the API-key holder identified by your JWT). No user_id to manage — your token *is* the identity. Thread isolation between customers is JWT-enforced.

Required. prompt OR messages (same shape as Chat). If both, messages wins.

Optional.
- provider, model, temperature, top_p, max_tokens, stop — passed through (same priority/failover as /v1/ai/chat).
- oauth_token / api_key — per-request credential override (same semantics as Chat).
- system — appended to (not replacing) the built-in narration prompt.
- retrieval — {enabled?: true, k?: 4, num_candidates?: 50, min_score?: null}. Set enabled: false to skip ES entirely.
- memory — {enabled?: true, reset?: false}. reset: true clears your history before this turn.

Rejected.
- json: true — incompatible with the conversational output style; use /v1/ai/chat.
- stream: true — not supported (memory persists after the full response).

Retrieval status (always answered; retrieval.status in response tells you what happened):
- ok — hits returned
- index_missing — no customer_{id}_documents index yet
- empty — index exists, no matching docs
- embedding_failed — fell back to BM25 only
- no_embed_provider — no provider with embed capability + creds; BM25-only fallback also unavailable
- error — ES network/cluster error
- disabled — caller passed retrieval.enabled: false or there was no user query to embed

Embeddings use OpenAI-compatible providers (openai, openrouter). Stored creds reused; no separate setup.

Memory cap. 30 messages or ~8000 estimated tokens, whichever hits first. Older turns drop off transparently.

Other example bodies.

Reset history mid-conversation:

{"prompt":"Let's start fresh — what's the weather like in Paris?","memory":{"reset":true}}

Disable retrieval (skip ES query, save 50-100ms):

{"prompt":"What's 2+2?","retrieval":{"enabled":false}}

With app-specific system guidance appended to the narration prompt:

{"prompt":"How do I refund?","system":"Always mention you're calling from EndpointrSupport.","retrieval":{"k":6}}

Minimal body: {"prompt":"How do I refund a charge?","retrieval":{"k":4}}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "How do I refund a charge?",
    "retrieval": {
        "k": 4
    }
}
curl -X POST 'https://api.endpointr.com/v1/ai/conversation' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "How do I refund a charge?",
    "retrieval": {
        "k": 4
    }
}'
const response = await fetch('https://api.endpointr.com/v1/ai/conversation', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "How do I refund a charge?",
      "retrieval": {
          "k": 4
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/conversation');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"How do I refund a charge?\",\n    \"retrieval\": {\n        \"k\": 4\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Image Generation

POST/v1/ai/image

Generate images from a prompt. Default uses openai + gpt-image-1 (a small, square 1024x1024 image), so the canonical body needs only a prompt.

Supported providers: openai, openrouter. The Claude/Codex subscriptions don't include image generation — claude-cli, openai-cli, and anthropic return 501.

OpenAI models: gpt-image-1 (default), dall-e-3. Optional fields: size (1024x1024 | 1792x1024 | 1024x1792 for dall-e-3), n (1-10), quality, style, response_format (url | b64_json).

OpenRouter models (delivered via /chat/completions with modalities: ["image","text"] under the hood):
- google/gemini-2.5-flash-image-preview — "Nano Banana," fast & cheap (default if you specify provider: openrouter and omit model).
- google/gemini-3-pro-image-preview — "Nano Banana Pro," higher quality.

Reference images. Pass reference_images as an array (max 16) of:
- public https URLs (jpeg / png / gif / webp), or
- data:image/png;base64,... URIs, or
- bare base64 payloads (PNG assumed by content sniff).

When present, OpenAI is routed through /v1/images/edits (multipart upload — gpt-image-1 accepts up to 16 references; dall-e-2 accepts 1; dall-e-3 does not support edits). OpenRouter passes them as image_url content parts on the user message — Gemini Nano Banana / Pro use them as visual context for the generation.

Response. {provider, model, images: [{url}|{b64_json}]}. OpenAI follows your response_format. OpenRouter always returns b64_json (data URIs are stripped server-side).

Other example bodies.

Explicit OpenAI with high quality + base64 response:

{"provider":"openai","model":"gpt-image-1","prompt":"A red fox in snow, photorealistic","size":"1024x1024","quality":"high","response_format":"b64_json"}

OpenRouter Gemini Nano Banana Pro:

{"provider":"openrouter","model":"google/gemini-3-pro-image-preview","prompt":"A red fox in snow, photorealistic"}

DALL-E 3 widescreen:

{"provider":"openai","model":"dall-e-3","prompt":"A futuristic cityscape at dusk","size":"1792x1024"}

Gemini Nano Banana with two reference images (style transfer):

{"provider":"openrouter","model":"google/gemini-2.5-flash-image-preview","prompt":"Render the subject from image 1 in the painterly style of image 2.","reference_images":["https://example.com/subject.jpg","https://example.com/style.jpg"]}

OpenAI gpt-image-1 edit with reference (composite multiple inputs):

{"provider":"openai","model":"gpt-image-1","prompt":"Combine these into one cohesive scene at golden hour.","reference_images":["https://example.com/a.png","https://example.com/b.png"]}

Minimal body: {"prompt":"A red fox in snow, photorealistic"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "A red fox in snow, photorealistic"
}
curl -X POST 'https://api.endpointr.com/v1/ai/image' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "A red fox in snow, photorealistic"
}'
const response = await fetch('https://api.endpointr.com/v1/ai/image', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "A red fox in snow, photorealistic"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/image');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"A red fox in snow, photorealistic\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

List Models

GET/v1/ai/models?provider=openrouter

Lists the models available to the authenticated customer for a given provider.

provider is optional — falls back to the priority list with failover. Supported: anthropic, openai, openrouter. claude-cli and openai-cli (subscription providers) don't expose a model catalog endpoint and return 501.

Minimal query: {"provider":"openrouter"}

AuthorizationBearer YOUR_JWT_TOKEN
provideropenrouter
curl -X GET 'https://api.endpointr.com/v1/ai/models?provider=openrouter' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/ai/models?provider=openrouter', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/models?provider=openrouter');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenAI-compatible

POST/v1/chat/completions

OpenAI-compatible chat/completions endpoint. Drop this in anywhere you'd point an OpenAI client (LangChain ChatOpenAI, the official openai SDK, n8n's *OpenAI* credential, etc.) and your customer api_key replaces the OpenAI key.

Path. Registered at both /v1/chat/completions (drop-in — n8n's default Base URL https://api.openai.com/v1 becomes https://api.endpointr.com/v1 with no path tweaking) and /v1/ai/completions (namespace match with the rest of /v1/ai/*). Same handler.

Auth. Authorization: Bearer <api_key> — the customer's raw api_key, not the JWT. No /v1/token exchange, no hourly refresh: n8n's OpenAI credential can't refresh on its own, so this endpoint uses the same flat-key model as OpenAI itself. SHA-256 hash lookup against customers.api_key_hash.

Response shape. Raw OpenAI envelope ({id, object, created, model, choices, usage}) — bypasses the standard Endpointr {httpCode, status, data, …} wrapper. Errors come back as {error: {message, type, code}}.

Model routing. Pick a model and the proxy picks the upstream:
- anthropic/<model> or bare claude-* → Anthropic API (uses the customer's vault anthropic.api_key). Full OpenAI ↔ Anthropic translation: messages, tools, tool_choice, tool_use blocks, system extraction, stop_reason mapping, usage field renaming.
- openai/<model> or bare gpt-* / o1-* / o3-* / o4-* → OpenAI API (vault openai.api_key). Near-passthrough — request/response barely touched.
- everything else → OpenRouter (vault openrouter.api_key). Passes the model string verbatim (google/gemini-2.5-flash, mistralai/mixtral-8x7b-instruct, etc.).

Missing creds for the resolved upstream → 401 with a clear message pointing at /admin/customers/{id} → service credentials.

Tool calling. Full support, required for n8n's AI Agent node. Send OpenAI-shape tools: [{type:"function", function:{name, description, parameters}}] and tool_choice — get back finish_reason: "tool_calls" with tool_calls[].function.arguments as a JSON-encoded string (this is what LangChain expects; do not parse it server-side). Continue the loop by sending a follow-up {role:"tool", tool_call_id, content} message.

Streaming. Set "stream": true to get back Content-Type: text/event-stream. Frames are chat.completion.chunk JSON terminated by data: [DONE]. For Anthropic this is full event-level translation (text deltas → delta.content, tool_use → delta.tool_calls[] with function.arguments arriving as concatenated string fragments — your client reassembles). Postman buffers SSE until end; use curl -N to watch live.

Accepted-but-ignored so n8n payloads don't trip a 400: logprobs, top_logprobs, n, seed, logit_bias, user, service_tier. Honored: model, messages, tools, tool_choice, stream, temperature, top_p, max_tokens, stop, response_format (OpenAI/OpenRouter only), presence_penalty / frequency_penalty (OpenAI/OpenRouter only).

Anthropic note. max_tokens is required upstream — we default to 4096 if you omit it.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "system",
            "content": "You are concise."
        },
        {
            "role": "user",
            "content": "Reply with exactly: pong"
        }
    ],
    "max_tokens": 50
}
curl -X POST 'https://api.endpointr.com/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "system",
            "content": "You are concise."
        },
        {
            "role": "user",
            "content": "Reply with exactly: pong"
        }
    ],
    "max_tokens": 50
}'
const response = await fetch('https://api.endpointr.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "model": "anthropic/claude-sonnet-4-6",
      "messages": [
          {
              "role": "system",
              "content": "You are concise."
          },
          {
              "role": "user",
              "content": "Reply with exactly: pong"
          }
      ],
      "max_tokens": 50
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/chat/completions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"model\": \"anthropic/claude-sonnet-4-6\",\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"You are concise.\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Reply with exactly: pong\"\n        }\n    ],\n    \"max_tokens\": 50\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/chat/completions

Tool-call round-trip. Send tools + a prompt that should trigger one. Expect 200 with choices[0].finish_reason: "tool_calls", choices[0].message.content: null, and choices[0].message.tool_calls[0].function.arguments as a JSON-encoded string (e.g. "{\"city\":\"Copenhagen\"}").

Continue the conversation with a follow-up request adding two messages: the assistant turn you just received, then {role:"tool", tool_call_id:"<the id>", content:"15°C, light rain"}. The next response will have finish_reason: "stop" and the model's final answer.

Works identically on anthropic/*, openai/*, and OpenRouter-routed models — only the upstream wire differs.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "user",
            "content": "What's the weather in Copenhagen?"
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Return current weather for a city.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "City name"
                        }
                    },
                    "required": [
                        "city"
                    ]
                }
            }
        }
    ],
    "tool_choice": "auto"
}
curl -X POST 'https://api.endpointr.com/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "user",
            "content": "What'\''s the weather in Copenhagen?"
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Return current weather for a city.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "City name"
                        }
                    },
                    "required": [
                        "city"
                    ]
                }
            }
        }
    ],
    "tool_choice": "auto"
}'
const response = await fetch('https://api.endpointr.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "model": "anthropic/claude-sonnet-4-6",
      "messages": [
          {
              "role": "user",
              "content": "What's the weather in Copenhagen?"
          }
      ],
      "tools": [
          {
              "type": "function",
              "function": {
                  "name": "get_weather",
                  "description": "Return current weather for a city.",
                  "parameters": {
                      "type": "object",
                      "properties": {
                          "city": {
                              "type": "string",
                              "description": "City name"
                          }
                      },
                      "required": [
                          "city"
                      ]
                  }
              }
          }
      ],
      "tool_choice": "auto"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/chat/completions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"model\": \"anthropic/claude-sonnet-4-6\",\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"What's the weather in Copenhagen?\"\n        }\n    ],\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"get_weather\",\n                \"description\": \"Return current weather for a city.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"city\": {\n                            \"type\": \"string\",\n                            \"description\": \"City name\"\n                        }\n                    },\n                    \"required\": [\n                        \"city\"\n                    ]\n                }\n            }\n        }\n    ],\n    \"tool_choice\": \"auto\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/chat/completions

Streaming variant. Same body as the non-streaming example with "stream": true. Returns Content-Type: text/event-stream and a sequence of data: {chat.completion.chunk} lines terminated by data: [DONE].

Postman buffers SSE bodies — you'll see the full stream after the request ends. To watch chunks live, use curl -N:

curl -N -X POST {{baseUrl}}/v1/chat/completions \
  -H "Authorization: Bearer {{api_key}}" \
  -H "Content-Type: application/json" \
  -d '{"model":"anthropic/claude-sonnet-4-6","messages":[{"role":"user","content":"Count from 1 to 5"}],"stream":true,"max_tokens":50}'

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "user",
            "content": "Count from 1 to 5"
        }
    ],
    "stream": true,
    "max_tokens": 50
}
curl -X POST 'https://api.endpointr.com/v1/chat/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "model": "anthropic/claude-sonnet-4-6",
    "messages": [
        {
            "role": "user",
            "content": "Count from 1 to 5"
        }
    ],
    "stream": true,
    "max_tokens": 50
}'
const response = await fetch('https://api.endpointr.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "model": "anthropic/claude-sonnet-4-6",
      "messages": [
          {
              "role": "user",
              "content": "Count from 1 to 5"
          }
      ],
      "stream": true,
      "max_tokens": 50
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/chat/completions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"model\": \"anthropic/claude-sonnet-4-6\",\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"Count from 1 to 5\"\n        }\n    ],\n    \"stream\": true,\n    \"max_tokens\": 50\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/ai/completions

Same handler as POST /v1/chat/completions — registered at this path too so it slots into the existing /v1/ai/* namespace alongside /v1/ai/chat, /v1/ai/stream, etc. Pick whichever path fits your routing.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "model": "openai/gpt-4o-mini",
    "messages": [
        {
            "role": "user",
            "content": "Say hi in one word."
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/ai/completions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
        {
            "role": "user",
            "content": "Say hi in one word."
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/ai/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "model": "openai/gpt-4o-mini",
      "messages": [
          {
              "role": "user",
              "content": "Say hi in one word."
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/completions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"model\": \"openai/gpt-4o-mini\",\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"Say hi in one word.\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Upload Image

POST/v1/ai/upload

Upload an image and get back a short-lived public URL you can pass to /v1/ai/vision as image_url.

Vision providers fetch image URLs from their own infrastructure (so the URL must be publicly reachable). This endpoint hands you exactly that — a URL on the Endpointr public uploads volume, valid for 30+ minutes.

Request:
- image_base64 — raw base64 (no data: URI prefix). The 1x1 PNG below is real and uploads cleanly; substitute your own bytes.
- format? — png | jpeg | webp | gif. Optional — magic bytes are sniffed and trusted over a caller hint.

Limits. Max raw size 25 MB. Allowed formats above.

Response. {url, mime, format, bytes, expires_in: 1800, expires_at: <iso8601>}. The 32-hex token in the URL has 128 bits of entropy; URL is the secret (no auth on download — necessary because vision providers can't carry your JWT).

TTL. 30 minutes minimum. A cron job in the backup container deletes files older than 30 minutes every 5 minutes — effective lifetime is 30-35 minutes. After that the URL 404s.

Required body: image_base64.

Minimal body: {"image_base64":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII="}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "format": "png"
}
curl -X POST 'https://api.endpointr.com/v1/ai/upload' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "format": "png"
}'
const response = await fetch('https://api.endpointr.com/v1/ai/upload', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
      "format": "png"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/upload');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"image_base64\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=\",\n    \"format\": \"png\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Video Generation

GET/v1/ai/video

List the 50 most recent video predictions for the authenticated customer (newest first). Same row shape as GET /v1/ai/video/{id}. Read-only — does not trigger any upstream poll.

_Requires stored credentials: atlascloud (PUT /v1/credentials/atlascloud)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/ai/video' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/ai/video', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/video');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/ai/video/:id

Poll a video generation by our internal id (returned from POST as id) — never AtlasCloud's prediction_id. Cross-customer access returns 404.

Behavior. If status is terminal (completed/failed/timeout), returns the cached row instantly without hitting upstream. If still processing, fires one upstream poll, updates the row, and returns. On status transition we fire VideoHandler.complete (success) or VideoHandler.failed (failure/timeout).

Response.

{"id":123,"provider":"atlascloud","model":"bytedance/seedance-2.0/text-to-video","prediction_id":"pred_abc","status":"completed","prompt":"...","outputs":["https://cdn.atlascloud.ai/.../video.mp4"],"error":null,"created_at":"...","completed_at":"..."}

Output URLs are AtlasCloud-hosted. Treat them as time-limited; download and re-host if you need durable storage.

_Requires stored credentials: atlascloud (PUT /v1/credentials/atlascloud)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/ai/video/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/ai/video/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/video/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/ai/video

Async video generation via AtlasCloud (fronts Google VEO 3.1 Lite/Fast/Pro and ByteDance Seedance 2.0).

Two-step flow. POST returns immediately with {id, prediction_id, status: "processing"}. Poll GET /v1/ai/video/{id} until status is completed, failed, or timeout. Completed predictions are cached server-side — subsequent GETs return instantly without hitting AtlasCloud.

Required body fields: model, prompt. The model string is forwarded verbatim to AtlasCloud.

URL-bearing fields (image, last_image, images[], reference_images[], reference_videos[], reference_audios[]) must be public https URLs or data: URIs. The server runs an SSRF check before forwarding — internal/private IPs are rejected as 400.

Param whitelist (union across families; AtlasCloud rejects per-model mismatches): duration, resolution, ratio/aspect_ratio, image, last_image, images, reference_images, reference_videos, reference_audios, generate_audio, web_search, watermark, return_last_frame, seed, negative_prompt. Anything outside this list is silently dropped.

Webhook events: VideoHandler.complete and VideoHandler.failed fire on the customer's poll that detects the transition (subscribe via POST /v1/webhooks). The standard .create/.get/.getAll events fire as usual.

Response. {id, provider:"atlascloud", model, prediction_id, status:"processing", created_at}. Save id and poll the GET endpoint.

---

Mode bodies:

*Seedance 2.0 — text-to-video (canonical):*

{"model":"bytedance/seedance-2.0/text-to-video","prompt":"A red fox running through snow at sunrise, cinematic.","ratio":"16:9","duration":5,"resolution":"1080p","generate_audio":true}

*Seedance 2.0 — image-to-video:*

{"model":"bytedance/seedance-2.0/image-to-video","prompt":"The subject turns slowly toward the camera.","image":"https://example.com/frame.jpg","ratio":"16:9","duration":5}

*Seedance 2.0 — reference-to-video (style + motion + audio):*

{"model":"bytedance/seedance-2.0/reference-to-video","prompt":"Match the references.","reference_images":["https://example.com/style.jpg"],"reference_videos":["https://example.com/motion.mp4"],"reference_audios":["https://example.com/music.mp3"]}

*Seedance 2.0 Fast variant* — same shapes with bytedance/seedance-2.0-fast/... (e.g. bytedance/seedance-2.0-fast/text-to-video).

*VEO 3.1 — text-to-video:*

{"model":"google/veo3.1/text-to-video","prompt":"Drone shot over alpine lake at golden hour.","aspect_ratio":"16:9","duration":8,"resolution":"1080p","seed":42,"negative_prompt":"blurry, low quality"}

*VEO 3.1 — image-to-video* (Lite / Fast / Pro):

{"model":"google/veo3.1-fast/image-to-video","prompt":"The subject slowly turns toward the camera.","image":"https://example.com/start.jpg","aspect_ratio":"16:9","duration":8}

*VEO 3.1 — start-end-frame-to-video* (Lite / Fast / Pro; both frames required):

{"model":"google/veo3.1/start-end-frame-to-video","prompt":"Smooth dolly between the two frames.","image":"https://example.com/start.jpg","last_image":"https://example.com/end.jpg","aspect_ratio":"16:9","duration":8}

*VEO 3.1 Pro — reference-to-video* (Pro tier only):

{"model":"google/veo3.1/reference-to-video","prompt":"Render the subject in the painterly style of the reference.","images":["https://example.com/subject.jpg","https://example.com/style.jpg"],"resolution":"1080p","generate_audio":true}

Errors. Create-time AtlasCloud 4xx/5xx surface as the same status with {error, upstream:{...}}. A *prediction* that fails after creation is not a create-time error — it surfaces on poll as status: "failed" with error text.

Minimal body: {"model":"bytedance/seedance-2.0/text-to-video","prompt":"A red fox running through snow at sunrise, cinematic.","ratio":"16:9","duration":5,"resolution":"1080p"}

_Requires stored credentials: atlascloud (PUT /v1/credentials/atlascloud)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "model": "bytedance/seedance-2.0/text-to-video",
    "prompt": "A red fox running through snow at sunrise, cinematic.",
    "ratio": "16:9",
    "duration": 5,
    "resolution": "1080p"
}
curl -X POST 'https://api.endpointr.com/v1/ai/video' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "model": "bytedance/seedance-2.0/text-to-video",
    "prompt": "A red fox running through snow at sunrise, cinematic.",
    "ratio": "16:9",
    "duration": 5,
    "resolution": "1080p"
}'
const response = await fetch('https://api.endpointr.com/v1/ai/video', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "model": "bytedance/seedance-2.0/text-to-video",
      "prompt": "A red fox running through snow at sunrise, cinematic.",
      "ratio": "16:9",
      "duration": 5,
      "resolution": "1080p"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/video');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"model\": \"bytedance/seedance-2.0/text-to-video\",\n    \"prompt\": \"A red fox running through snow at sunrise, cinematic.\",\n    \"ratio\": \"16:9\",\n    \"duration\": 5,\n    \"resolution\": \"1080p\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Vision

POST/v1/ai/vision

Image + text in, text out. Same provider/credential semantics as /v1/ai/chat.

Supported providers: claude-cli, openai, anthropic, openrouter. openai-cli does not support vision (returns 501).

image_url is either:
- a public https URL (jpeg / png / gif / webp), or
- a data:image/png;base64,... URI for inline images, or
- the URL handed back by POST /v1/ai/upload (recommended for client-side uploads — see that endpoint).

Other example bodies.

Explicit Anthropic API:

{"provider":"anthropic","model":"claude-sonnet-4-6","prompt":"Describe what you see.","image_url":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg","max_tokens":500}

Claude-CLI with body-supplied token:

{"provider":"claude-cli","model":"claude-sonnet-4-6","oauth_token":"{{claude_oauth_token}}","prompt":"What is in this image?","image_url":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg","max_tokens":500}

OpenAI with a data-URI image (handy for tiny images you don't want to host):

{"provider":"openai","model":"gpt-4o-mini","prompt":"What's in this image?","image_url":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII="}

Minimal body: {"prompt":"What is in this image?","image_url":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg","max_tokens":500}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "prompt": "What is in this image?",
    "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg",
    "max_tokens": 500
}
curl -X POST 'https://api.endpointr.com/v1/ai/vision' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "prompt": "What is in this image?",
    "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg",
    "max_tokens": 500
}'
const response = await fetch('https://api.endpointr.com/v1/ai/vision', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "prompt": "What is in this image?",
      "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg",
      "max_tokens": 500
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/ai/vision');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"prompt\": \"What is in this image?\",\n    \"image_url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/640px-Cat_November_2010-1a.jpg\",\n    \"max_tokens\": 500\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Webhooks

GET/v1/webhooks/events

List every event name that a handler can emit.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/webhooks/events' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/webhooks/events', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks/events');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/webhooks

List webhooks registered under your account.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/webhooks

Register a webhook URL for an event.

Response includes a secret (returned once) — receivers verify HMAC-SHA256 against the JSON body via the X-Endpointr-Signature: sha256=… header.

Meta Marketing events fired by the inbound /v1/webhooks/inbound/meta-ads endpoint use names of the shape MetaAds.<object>.<field> — e.g. MetaAds.page.leadgen, MetaAds.ad_account.adsstatus. Subscribe to those exactly the same way as any other event name.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "event": "HeaderMarkdownExtractor.getByParam",
    "url": "https://receiver.example.com/webhook"
}
curl -X POST 'https://api.endpointr.com/v1/webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "event": "HeaderMarkdownExtractor.getByParam",
    "url": "https://receiver.example.com/webhook"
}'
const response = await fetch('https://api.endpointr.com/v1/webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "event": "HeaderMarkdownExtractor.getByParam",
      "url": "https://receiver.example.com/webhook"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"event\": \"HeaderMarkdownExtractor.getByParam\",\n    \"url\": \"https://receiver.example.com/webhook\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/webhooks/:id

Delete one of your webhooks.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/webhooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/webhooks/inbound/meta-ads?hub.mode=subscribe&hub.verify_token=%E2%80%A6&hub.challenge=12345
no auth this endpoint does not require a Bearer token.

Meta's webhook subscription handshake. NOT JWT-protected — Meta hits this with hub.mode=subscribe&hub.verify_token=…&hub.challenge=… and expects the challenge echoed back as plain text.

The verify token is set app-wide via the META_WEBHOOK_VERIFY_TOKEN env var (must match what you pass to POST /v1/marketing/webhook-subscriptions). 403 on mismatch.

hub.modesubscribe
hub.verify_token…
hub.challenge12345
curl -X GET 'https://api.endpointr.com/v1/webhooks/inbound/meta-ads?hub.mode=subscribe&hub.verify_token=%E2%80%A6&hub.challenge=12345'
const response = await fetch('https://api.endpointr.com/v1/webhooks/inbound/meta-ads?hub.mode=subscribe&hub.verify_token=%E2%80%A6&hub.challenge=12345', {
  method: 'GET'
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks/inbound/meta-ads?hub.mode=subscribe&hub.verify_token=%E2%80%A6&hub.challenge=12345');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/webhooks/inbound/meta-ads
no auth this endpoint does not require a Bearer token.

Meta's webhook delivery endpoint. NOT JWT-protected — authenticity is verified via HMAC-SHA256 of the raw body against the per-customer app_secret (stored at slug meta-ads).

Flow per delivery: persist raw event → resolve customer by matching entry[].id against meta_ad_webhook_subs.object_id → verify HMAC → fan out via the standard WebhookManager (event name MetaAds.<object>.<field>) → enqueue a leadgen_fetch worker job for any leadgen field changes (so the full lead body lands in meta_ad_leads).

Always responds 200 — even on signature mismatch or unknown object_id — to prevent Meta retry storms. Forensics: unmatched/unverified deliveries are kept in meta_ad_webhook_events with customer_id=NULL for audit.

curl -X POST 'https://api.endpointr.com/v1/webhooks/inbound/meta-ads'
const response = await fetch('https://api.endpointr.com/v1/webhooks/inbound/meta-ads', {
  method: 'POST'
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/webhooks/inbound/meta-ads');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Profylr

Company lookup

GET/v1/profylr/company?domain=example.com&country=dk&refresh=false

Implemented. Resolve a domain to a company profile built from two independent legs:

1. Crawler leg — /v1/crawlers/* waterfall (social-profiles → phone → email → cvr → cms) against the website. Zero API tokens unless SCRAPEDO_API_KEY is set, in which case fetches go through scrape.do (spends credits — needed to clear Cloudflare managed-challenge pages).
2. Enrichment leg — the customer's admin-ordered enrichment_priority list is walked with a company-shaped query; the first provider that returns a hit wins. The raw response lands at company.enrichment.data._raw for inspection while we map fields.

Renamed from /v1/profylr/domain. Endpointr now treats contact (person) and company as two top-level entities — same async machinery, separate routes.

Selectors.
- ?domain=example.com (or ?url=https://example.com/anything — the host is extracted). One is required; a bad/missing value → 400.
- ?country=dk|no|se|… (optional, ISO-2). A .com may actually be a Danish/Swedish/Norwegian company; passing country does two things: it tells the CVR crawler which org-number format to look for (DK 8-digit CVR / NO 9-digit / SE 10-digit, all prefix-guarded against PII false positives) and it sets scrape.do's geoCode so the page is fetched from that country's IP (helps when content is geo-routed). Different countries are *different* enqueued jobs (separate idempotency).
- ?refresh=true (also ?bypass_cache=true / ?force=true) bypasses the TTL freshness check and enqueues a fresh crawl regardless. A cached company doc, if present, is still returned in this response (marked freshness: "stale"); the refreshed copy will land on the next poll.

Async (profylr_jobs). Both legs run in the enrichment worker, so the response is immediate. freshness is reconciled against the job queue's real state — pending/stale are only ever reported while a job is genuinely queued or running:
- pending — no data yet; a company_crawl job is queued/running. Poll again, or use sync=1.
- stale — cached doc returned now; a refresh is queued/running.
- fresh — cached within TTL (PROFYLR_DOMAIN_TTL_DAYS, default 30); nothing enqueued.
- not_found — terminal. The pipeline completed (or no provider is runnable for this query) and no data was found. Polling will not change the answer; refresh=1 re-runs the work.
- failed — terminal. The background job exhausted its retries. Plain polls do NOT revive it (that would retry forever); refresh=1 revives explicitly.

Sync mode. ?sync=1 (alias ?wait=1) holds the request open until the enqueued job finishes — ?timeout= seconds, 1-25, default 20 — and returns the final profile in one round-trip. The response carries sync: {requested, waited_ms, outcome: completed|failed|timeout|no_wait}. Requires the enrichment worker to be running; without it the wait times out and the regular async answer is returned.

data: {domain, country, refresh, freshness, found, job_status, enqueued, es_status, company, checked_at, note}. found says whether the doc carries actual findings (not just bookkeeping); job_status mirrors the queue row (pending|claimed|completed|failed) or null. company is the profylr_companies doc (or null until the first crawl lands) with the crawler output under web_crawl and the provider waterfall under enrichment.

403 if the domain is in profylr_optouts (when PROFYLR_ENFORCE_OPTOUT=true).

Required query: domain.

Minimal query: {"domain":"example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
domainexample.com
countrydk
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/profylr/company?domain=example.com&country=dk&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/profylr/company?domain=example.com&country=dk&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/profylr/company?domain=example.com&country=dk&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

DSR (export / erase)

POST/v1/profylr/dsr

Specified, not yet implemented — present so the collection documents the Phase 1 contract (docs/api.md).

Data Subject Request. operation: export (full machine-readable bundle of every field held, with provenance) or erase (tombstone + removal across all ES indices and the job queue). identifier is an email, LinkedIn slug/URL, GitHub username, or person_id.

202 accepted — processed asynchronously and audited · 401 missing/invalid/revoked JWT · 404 no such data subject. Runbook: docs/compliance/dsr-handling-runbook.md.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "operation": "export",
    "identifier": "jane@example.com"
}
curl -X POST 'https://api.endpointr.com/v1/profylr/dsr' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "operation": "export",
    "identifier": "jane@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/profylr/dsr', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "operation": "export",
      "identifier": "jane@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/profylr/dsr');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"operation\": \"export\",\n    \"identifier\": \"jane@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Opt-out

POST/v1/profylr/opt-out

Specified, not yet implemented — present so the collection documents the Phase 1 contract (docs/api.md).

Register an opt-out. scope ∈ email | domain | person; value is the address / domain / person_id. Omit channels to opt out of every channel, or pass an array of channel keys to scope it. reason is free text for the audit trail.

201 recorded · 401 missing/invalid/revoked JWT. Enforced by the lookup API and every drafter; writes a profylr_audit row.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "scope": "email",
    "value": "jane@example.com",
    "channels": [
        "email",
        "linkedin"
    ],
    "reason": "Subject requested removal"
}
curl -X POST 'https://api.endpointr.com/v1/profylr/opt-out' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "scope": "email",
    "value": "jane@example.com",
    "channels": [
        "email",
        "linkedin"
    ],
    "reason": "Subject requested removal"
}'
const response = await fetch('https://api.endpointr.com/v1/profylr/opt-out', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "scope": "email",
      "value": "jane@example.com",
      "channels": [
          "email",
          "linkedin"
      ],
      "reason": "Subject requested removal"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/profylr/opt-out');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"scope\": \"email\",\n    \"value\": \"jane@example.com\",\n    \"channels\": [\n        \"email\",\n        \"linkedin\"\n    ],\n    \"reason\": \"Subject requested removal\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Person lookup

GET/v1/profylr/person?email=jane%40example.com&country=dk&refresh=false

Implemented (email selector). Resolve a person by email and attach the token-free domain crawl as the basis.

Selectors.
- ?email=jane@acme.com (required). Validated; the canonical person doc is keyed by email (a ULID person_id is minted; deterministic dedupe by email). ?linkedin=, ?github=, ?name=&company= are in docs/api.md but return a clear 400 in this phase (probabilistic identity *merge* is deferred).
- ?country=dk|no|se|… (optional, ISO-2). Forwarded to the domain crawl (CVR pattern + scrape.do geoCode).
- ?refresh=true bypasses the TTL freshness check and enqueues a fresh crawl.

Email → domain. If the email domain is a generic free-mail provider (gmail, outlook, yahoo, proton, …; extend via PROFYLR_GENERIC_EMAIL_DOMAINS), the waterfall is skipped (domain_enrichment: "skipped_generic"). Otherwise the employer domain is crawled asynchronously and linked.

Async (profylr_jobs). Same freshness model as Company lookup, reconciled against the job queue's real state: pending/stale only while a job is genuinely queued or running; not_found (pipeline completed, nothing found) and failed (job exhausted retries) are terminal — polling will not change them, refresh=1 re-runs. Provider slugs the worker cannot actually attempt (unwired / no stored credentials / unsupported query shape) never count toward freshness, so the endpoint cannot ask you to poll for work that will never run.

Sync mode. ?sync=1 (alias ?wait=1) holds the request open until the job finishes — ?timeout= seconds, 1-25, default 20 — and returns the final profile in one round-trip; the response carries sync: {requested, waited_ms, outcome: completed|failed|timeout|no_wait}. Requires the enrichment worker to be running.

data: {email, domain, domain_generic, domain_enrichment, email_enrichment, country, refresh, freshness, found, job_status, enqueued, es_status, person, checked_at, note}. personal_context is never returned (hard rule).

403 if the email or its domain is opted out.

Required query: email.

Minimal query: {"email":"jane@example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
emailjane@example.com
countrydk
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/profylr/person?email=jane%40example.com&country=dk&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/profylr/person?email=jane%40example.com&country=dk&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/profylr/person?email=jane%40example.com&country=dk&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ping (bridge health check)

GET/v1/profylr/ping

Implemented. Bridge smoke test for the Profylr module: proves composer PSR-4 autoload, the reflection-router route, and the shared endpointr JWT middleware all line up. Returns the standard envelope with data: {profylr:"ok", version, php, customer_id, generated_at}.

Auth. Standard endpointr JWT — Authorization: Bearer {{token}} (issue via POST /v1/token with your api_key). No/expired/revoked JWT → 401. This is the *same* JWT as the rest of /v1/*; Profylr is not on profylr.endpointr.com (that vhost is the operator console only).

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/profylr/ping' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/profylr/ping', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/profylr/ping');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Payments

Clearhaus Settlements

GET/v1/payments/clearhaus-settlements

List settlements.

_Requires stored credentials: clearhaus (PUT /v1/credentials/clearhaus)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/clearhaus-settlements' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/clearhaus-settlements', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/clearhaus-settlements');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/payments/clearhaus-settlements/:id

Fetch settlement by id.

_Requires stored credentials: clearhaus (PUT /v1/credentials/clearhaus)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/clearhaus-settlements/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/clearhaus-settlements/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/clearhaus-settlements/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Accounts

GET/v1/payments/paypal-accounts

List the configured PayPal account labels (stored as paypal:<label>). Pass one as account on any PayPal tool. No upstream call.

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Balance (money on account)

GET/v1/payments/paypal-balance

Current available balance per currency — how much money is on the account. Optional query: currency_code, as_of_time (RFC3339, historical). Needs the app's Transaction Search feature; can lag live by ~3h.

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-balance' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-balance', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-balance');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Billing Plans (search subscriptions by name)

GET/v1/payments/paypal-plans/:id

Get one billing plan by its id (P-…) — name, description, status, billing cycles, pricing.

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-plans/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-plans/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-plans/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/payments/paypal-plans?name=Pro&page_size=20

List billing plans — the NAMED recurring products. This is how you search subscriptions by name/description: pass name/description (filtered client-side within the fetched page; widen with page_size/page). Passthrough: product_id, page, page_size, total_required. Then read a subscriber's subscription via the subscriptions get.

Minimal query: {"name":"Pro","page_size":20}

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
namePro
page_size20
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-plans?name=Pro&page_size=20' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-plans?name=Pro&page_size=20', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-plans?name=Pro&page_size=20');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Invoices

GET/v1/payments/paypal-invoices/:id

Get one invoice by its id (e.g. INV2-XXXX-XXXX-XXXX-XXXX).

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/payments/paypal-invoices?status=UNPAID&page_size=20&total_required=true

List invoices, or search when you pass filters. Filters (search): status (PAID|MARKED_AS_PAID|PARTIALLY_PAID|SENT|UNPAID|PAYMENT_PENDING|SCHEDULED|CANCELLED|REFUNDED), invoice_date_range/due_date_range ({start,end}), recipient_email, total_amount_range, invoice_number. Pagination: page, page_size, total_required. Overdue = outstanding past its due date (filter due_date_range, or use Overview). account selects the PayPal account.

Minimal query: {"status":"UNPAID","page_size":20,"total_required":"true"}

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
statusUNPAID
page_size20
total_requiredtrue
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-invoices?status=UNPAID&page_size=20&total_required=true' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-invoices?status=UNPAID&page_size=20&total_required=true', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-invoices?status=UNPAID&page_size=20&total_required=true');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Overview (income snapshot)

GET/v1/payments/paypal-overview

One-call money snapshot: current balances, invoices bucketed paid / outstanding / overdue, and gross income over the last 30 days, grouped per currency. Start here. Balance/income need the app's Transaction Search feature; each source degrades independently into warnings. account selects which PayPal account (multi-account).

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-overview' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-overview', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-overview');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PayPal — Subscriptions

GET/v1/payments/paypal-subscriptions/:id

Get one subscription by its id (I-…): status, plan_id, subscriber, billing_info (next billing time, last payment, outstanding balance). PayPal has no list/search for subscriptions — find the plan by name via the plans tool, then read a subscriber's sub by its I-… id.

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-subscriptions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/payments/paypal-transactions?start_date=2026-06-01T00%3A00%3A00Z&end_date=2026-06-30T23%3A59%3A59Z&fields=all

Transaction Search — the true overall income source (all payments received). Required: start_date + end_date (RFC3339), max 31 days apart; page by month for longer ranges. Optional: transaction_status (S|P|D|V), fields (default all), page, page_size (max 500). Needs the app's Transaction Search feature; data lags ~3h.

Minimal query: {"start_date":"2026-06-01T00:00:00Z","end_date":"2026-06-30T23:59:59Z","fields":"all"}

_Requires stored credentials: paypal (PUT /v1/credentials/paypal)._

AuthorizationBearer YOUR_JWT_TOKEN
start_date2026-06-01T00:00:00Z
end_date2026-06-30T23:59:59Z
fieldsall
curl -X GET 'https://api.endpointr.com/v1/payments/paypal-transactions?start_date=2026-06-01T00%3A00%3A00Z&end_date=2026-06-30T23%3A59%3A59Z&fields=all' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/payments/paypal-transactions?start_date=2026-06-01T00%3A00%3A00Z&end_date=2026-06-30T23%3A59%3A59Z&fields=all', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/payments/paypal-transactions?start_date=2026-06-01T00%3A00%3A00Z&end_date=2026-06-30T23%3A59%3A59Z&fields=all');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Stripe

Accounting

Billy — Account groups

GET/v1/accounting/billy-account-groups/:id

Get one of accountGroups by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-account-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-account-groups?pageSize=100

List accountGroups. Chart-of-accounts groups (each account belongs to a group, each group to a nature). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. accountGroups.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-account-groups?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-groups?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-groups?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-account-groups

Create in accountGroups. Send the record fields directly (the relay wraps them in Billy's accountGroup root key and adds organizationId). Required: name, natureId.

Minimal body: {"name":"Software","natureId":"<nature-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Software",
    "natureId": "<nature-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-account-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Software",
    "natureId": "<nature-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Software",
      "natureId": "<nature-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Software\",\n    \"natureId\": \"<nature-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-account-groups/:id

Update one of accountGroups by {id} (partial: only the fields sent change).

Minimal body: {"name":"Software"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Software"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-account-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Software"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Software"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Software\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-account-groups/:id

Delete one of accountGroups by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-account-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-groups/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Account natures

GET/v1/accounting/billy-account-natures/:id

Get one of accountNatures by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-account-natures/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-natures/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-natures/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-account-natures?pageSize=100

List accountNatures. The natures behind account groups (reportType revenue/expense/asset/liability/equity, normalBalance debit/credit). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. accountNatures.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-account-natures?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-natures?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-natures?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-account-natures

Create in accountNatures. Send the record fields directly (the relay wraps them in Billy's accountNature root key and adds organizationId). Required: name.

Minimal body: {"name":"Expenses","reportType":"expense","normalBalance":"debit"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Expenses",
    "reportType": "expense",
    "normalBalance": "debit"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-account-natures' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Expenses",
    "reportType": "expense",
    "normalBalance": "debit"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-natures', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Expenses",
      "reportType": "expense",
      "normalBalance": "debit"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-natures');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Expenses\",\n    \"reportType\": \"expense\",\n    \"normalBalance\": \"debit\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-account-natures/:id

Update one of accountNatures by {id} (partial: only the fields sent change).

Minimal body: {"name":"Expenses"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Expenses"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-account-natures/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Expenses"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-account-natures/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Expenses"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-account-natures/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Expenses\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Accounts

GET/v1/accounting/billy-accounts/:id

Get one of accounts by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-accounts?pageSize=100

List accounts. The chart of accounts. isBankAccount=true finds the bank accounts bank lines need. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. accounts.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-accounts?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-accounts?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-accounts?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-accounts

Create in accounts. Send the record fields directly (the relay wraps them in Billy's account root key and adds organizationId). Required: name, groupId.

Minimal body: {"name":"Hosting","groupId":"<group-id>","accountNo":7320}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting",
    "groupId": "<group-id>",
    "accountNo": 7320
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting",
    "groupId": "<group-id>",
    "accountNo": 7320
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting",
      "groupId": "<group-id>",
      "accountNo": 7320
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting\",\n    \"groupId\": \"<group-id>\",\n    \"accountNo\": 7320\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-accounts/:id

Update one of accounts by {id} (partial: only the fields sent change).

Minimal body: {"name":"Hosting"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-accounts/:id

Delete one of accounts by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-accounts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Attachments

GET/v1/accounting/billy-attachments/:id

Get one of attachments by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-attachments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-attachments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-attachments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-attachments?pageSize=100

List attachments. Files linked to a record (ownerReference like invoice:<id>). Filters: unhandled, handled, ownerReference, type, supplier, amount, documentDatePeriod, isDuplicate, eInvoiceFilter, q. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. attachments.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-attachments?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-attachments?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-attachments?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-attachments

Create in attachments. Send the record fields directly (the relay wraps them in Billy's attachment root key and adds organizationId). Required: ownerReference, fileId.

Minimal body: {"ownerReference":"bill:<bill-id>","fileId":"<file-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ownerReference": "bill:<bill-id>",
    "fileId": "<file-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-attachments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ownerReference": "bill:<bill-id>",
    "fileId": "<file-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-attachments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ownerReference": "bill:<bill-id>",
      "fileId": "<file-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-attachments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ownerReference\": \"bill:<bill-id>\",\n    \"fileId\": \"<file-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-attachments/:id

Update one of attachments by {id} (partial: only the fields sent change).

Minimal body: {"ownerReference":"bill:<bill-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ownerReference": "bill:<bill-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-attachments/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ownerReference": "bill:<bill-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-attachments/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ownerReference": "bill:<bill-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-attachments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ownerReference\": \"bill:<bill-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-attachments/:id

Delete one of attachments by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-attachments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-attachments/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-attachments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Balance modifiers

GET/v1/accounting/billy-balance-modifiers/:id

Get one of balanceModifiers by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-balance-modifiers?pageSize=100

List balanceModifiers. Links between a modifier (payment) and a subject (invoice/bill) that change a balance. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. balanceModifiers.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-balance-modifiers?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-balance-modifiers?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-balance-modifiers?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-balance-modifiers

Create in balanceModifiers. Send the record fields directly (the relay wraps them in Billy's balanceModifier root key and adds organizationId). Required: modifierReference, subjectReference.

Minimal body: {"modifierReference":"bankPayment:<id>","subjectReference":"invoice:<id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "modifierReference": "bankPayment:<id>",
    "subjectReference": "invoice:<id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-balance-modifiers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "modifierReference": "bankPayment:<id>",
    "subjectReference": "invoice:<id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-balance-modifiers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "modifierReference": "bankPayment:<id>",
      "subjectReference": "invoice:<id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-balance-modifiers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"modifierReference\": \"bankPayment:<id>\",\n    \"subjectReference\": \"invoice:<id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-balance-modifiers/:id

Update one of balanceModifiers by {id} (partial: only the fields sent change).

Minimal body: {"modifierReference":"bankPayment:<id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "modifierReference": "bankPayment:<id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "modifierReference": "bankPayment:<id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "modifierReference": "bankPayment:<id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-balance-modifiers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"modifierReference\": \"bankPayment:<id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bank line matches

GET/v1/accounting/billy-bank-line-matches/:id

Get one of bankLineMatches by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bank-line-matches?pageSize=100

List bankLineMatches. A reconciliation match on a bank account (bank lines ↔ postings). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. bankLineMatches.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-line-matches?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-matches?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-matches?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bank-line-matches

Create in bankLineMatches. Send the record fields directly (the relay wraps them in Billy's bankLineMatch root key and adds organizationId). Required: accountId.

Minimal body: {"accountId":"<bank-account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<bank-account-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bank-line-matches' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<bank-account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-matches', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<bank-account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-matches');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<bank-account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bank-line-matches/:id

Update one of bankLineMatches by {id} (partial: only the fields sent change).

Minimal body: {"accountId":"<bank-account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<bank-account-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<bank-account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<bank-account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<bank-account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bank-line-matches/:id

Delete one of bankLineMatches by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-matches/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bank line subject associations

GET/v1/accounting/billy-bank-line-subject-associations/:id

Get one of bankLineSubjectAssociations by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bank-line-subject-associations?pageSize=100

List bankLineSubjectAssociations. Which subject (invoice, bill, posting) a bank line match settles. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. bankLineSubjectAssociations.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bank-line-subject-associations

Create in bankLineSubjectAssociations. Send the record fields directly (the relay wraps them in Billy's bankLineSubjectAssociation root key and adds organizationId). Required: matchId, subjectReference.

Minimal body: {"matchId":"<match-id>","subjectReference":"invoice:<id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "matchId": "<match-id>",
    "subjectReference": "invoice:<id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "matchId": "<match-id>",
    "subjectReference": "invoice:<id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "matchId": "<match-id>",
      "subjectReference": "invoice:<id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"matchId\": \"<match-id>\",\n    \"subjectReference\": \"invoice:<id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bank-line-subject-associations/:id

Update one of bankLineSubjectAssociations by {id} (partial: only the fields sent change).

Minimal body: {"matchId":"<match-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "matchId": "<match-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "matchId": "<match-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "matchId": "<match-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"matchId\": \"<match-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bank-line-subject-associations/:id

Delete one of bankLineSubjectAssociations by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-line-subject-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bank lines

GET/v1/accounting/billy-bank-lines/:id

Get one of bankLines by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bank-lines?pageSize=100

List bankLines. Imported bank statement lines. accountId is REQUIRED on list (find bank accounts with accounts isBankAccount=true); filters isReconciled, status (pending|booked), side, externalId, receiptState, minAmount, maxAmount, minEntryDate, maxEntryDate, entryDatePeriod, q. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. bankLines.contact:embed), q.

Required query: accountId.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-lines?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-lines?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-lines?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bank-lines

Create in bankLines. Send the record fields directly (the relay wraps them in Billy's bankLine root key and adds organizationId). Required: matchId, accountId, entryDate, description, amount, side.

Minimal body: {"accountId":"<bank-account-id>","entryDate":"2026-09-01","description":"Stripe payout","amount":1200,"side":"debit"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<bank-account-id>",
    "entryDate": "2026-09-01",
    "description": "Stripe payout",
    "amount": 1200,
    "side": "debit"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bank-lines' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<bank-account-id>",
    "entryDate": "2026-09-01",
    "description": "Stripe payout",
    "amount": 1200,
    "side": "debit"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-lines', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<bank-account-id>",
      "entryDate": "2026-09-01",
      "description": "Stripe payout",
      "amount": 1200,
      "side": "debit"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-lines');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<bank-account-id>\",\n    \"entryDate\": \"2026-09-01\",\n    \"description\": \"Stripe payout\",\n    \"amount\": 1200,\n    \"side\": \"debit\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bank-lines/:id

Update one of bankLines by {id} (partial: only the fields sent change).

Minimal body: {"accountId":"<bank-account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<bank-account-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bank-lines/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<bank-account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<bank-account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<bank-account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bank-lines/:id

Delete one of bankLines by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bank-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bank payments

GET/v1/accounting/billy-bank-payments/:id

Get one of bankPayments by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-payments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bank-payments?pageSize=100

List bankPayments. Register a payment against invoices or bills: cashSide debit = money in (invoices), credit = money out (bills); associations[].subjectReference = invoice:<id> / bill:<id>, all in the same currency. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. bankPayments.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bank-payments?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-payments?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-payments?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bank-payments

Create in bankPayments. Send the record fields directly (the relay wraps them in Billy's bankPayment root key and adds organizationId). Required: entryDate, cashAmount, cashSide, cashAccountId, associations.

Minimal body: {"entryDate":"2026-09-15","cashAmount":1200,"cashSide":"debit","cashAccountId":"<bank-account-id>","associations":[{"subjectReference":"invoice:<invoice-id>"}]}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entryDate": "2026-09-15",
    "cashAmount": 1200,
    "cashSide": "debit",
    "cashAccountId": "<bank-account-id>",
    "associations": [
        {
            "subjectReference": "invoice:<invoice-id>"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bank-payments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entryDate": "2026-09-15",
    "cashAmount": 1200,
    "cashSide": "debit",
    "cashAccountId": "<bank-account-id>",
    "associations": [
        {
            "subjectReference": "invoice:<invoice-id>"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-payments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entryDate": "2026-09-15",
      "cashAmount": 1200,
      "cashSide": "debit",
      "cashAccountId": "<bank-account-id>",
      "associations": [
          {
              "subjectReference": "invoice:<invoice-id>"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-payments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entryDate\": \"2026-09-15\",\n    \"cashAmount\": 1200,\n    \"cashSide\": \"debit\",\n    \"cashAccountId\": \"<bank-account-id>\",\n    \"associations\": [\n        {\n            \"subjectReference\": \"invoice:<invoice-id>\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bank-payments/:id

Update one of bankPayments by {id} (partial: only the fields sent change).

Minimal body: {"entryDate":"2026-09-15"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entryDate": "2026-09-15"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bank-payments/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entryDate": "2026-09-15"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entryDate": "2026-09-15"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entryDate\": \"2026-09-15\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bank-payments/:id

Delete one of bankPayments by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bank-payments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bank-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bill lines

GET/v1/accounting/billy-bill-lines/:id

Get one of billLines by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bill-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bill-lines?pageSize=100

List billLines. Lines of a supplier bill. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. billLines.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bill-lines?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bill-lines?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bill-lines?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bill-lines

Create in billLines. Send the record fields directly (the relay wraps them in Billy's billLine root key and adds organizationId). Required: billId, accountId, taxRateId, description, amount.

Minimal body: {"billId":"<bill-id>","accountId":"<account-id>","taxRateId":"<tax-rate-id>","description":"Hosting september","amount":800}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "billId": "<bill-id>",
    "accountId": "<account-id>",
    "taxRateId": "<tax-rate-id>",
    "description": "Hosting september",
    "amount": 800
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bill-lines' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "billId": "<bill-id>",
    "accountId": "<account-id>",
    "taxRateId": "<tax-rate-id>",
    "description": "Hosting september",
    "amount": 800
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bill-lines', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "billId": "<bill-id>",
      "accountId": "<account-id>",
      "taxRateId": "<tax-rate-id>",
      "description": "Hosting september",
      "amount": 800
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bill-lines');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"billId\": \"<bill-id>\",\n    \"accountId\": \"<account-id>\",\n    \"taxRateId\": \"<tax-rate-id>\",\n    \"description\": \"Hosting september\",\n    \"amount\": 800\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bill-lines/:id

Update one of billLines by {id} (partial: only the fields sent change).

Minimal body: {"billId":"<bill-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "billId": "<bill-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bill-lines/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "billId": "<bill-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "billId": "<bill-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"billId\": \"<bill-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bill-lines/:id

Delete one of billLines by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bill-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bill-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Bills (supplier invoices)

GET/v1/accounting/billy-bills/:id

Get one bill by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bills/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bills/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bills/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-bills?state=approved&pageSize=100

List supplier bills (documented filters allow-listed: contactId, creditedBillId, minEntryDate, maxEntryDate, minDueDate, maxDueDate, isPaid, hasAttachments, state draft|approved|voided, currencyId, suppliersInvoiceNo, isBare, amount, q, sortProperty, sortDirection, include, page, pageSize).

Minimal query: {"state":"approved","pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
stateapproved
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-bills?state=approved&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bills?state=approved&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bills?state=approved&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-bills

Create a DRAFT bill (record fields; lines may be embedded, each {accountId, taxRateId, description, amount}). Via action: approve (id) books it; credit_note (id of the bill to credit) creates a credit note. Required: contactId, entryDate, paymentDate.

Minimal body: {"contactId":"<supplier-contact-id>","entryDate":"2026-09-01","paymentDate":"2026-09-30","suppliersInvoiceNo":"INV-123","lines":[{"accountId":"<account-id>","taxRateId":"<tax-rate-id>","description":"Hosting","amount":800}]}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contactId": "<supplier-contact-id>",
    "entryDate": "2026-09-01",
    "paymentDate": "2026-09-30",
    "suppliersInvoiceNo": "INV-123",
    "lines": [
        {
            "accountId": "<account-id>",
            "taxRateId": "<tax-rate-id>",
            "description": "Hosting",
            "amount": 800
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-bills' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contactId": "<supplier-contact-id>",
    "entryDate": "2026-09-01",
    "paymentDate": "2026-09-30",
    "suppliersInvoiceNo": "INV-123",
    "lines": [
        {
            "accountId": "<account-id>",
            "taxRateId": "<tax-rate-id>",
            "description": "Hosting",
            "amount": 800
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bills', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contactId": "<supplier-contact-id>",
      "entryDate": "2026-09-01",
      "paymentDate": "2026-09-30",
      "suppliersInvoiceNo": "INV-123",
      "lines": [
          {
              "accountId": "<account-id>",
              "taxRateId": "<tax-rate-id>",
              "description": "Hosting",
              "amount": 800
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bills');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contactId\": \"<supplier-contact-id>\",\n    \"entryDate\": \"2026-09-01\",\n    \"paymentDate\": \"2026-09-30\",\n    \"suppliersInvoiceNo\": \"INV-123\",\n    \"lines\": [\n        {\n            \"accountId\": \"<account-id>\",\n            \"taxRateId\": \"<tax-rate-id>\",\n            \"description\": \"Hosting\",\n            \"amount\": 800\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-bills/:id

Update a draft bill by {id} (partial).

Minimal body: {"paymentDate":"2026-10-05"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "paymentDate": "2026-10-05"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-bills/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "paymentDate": "2026-10-05"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bills/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "paymentDate": "2026-10-05"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bills/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"paymentDate\": \"2026-10-05\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-bills/:id

Delete a draft bill by {id} (idempotent).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-bills/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-bills/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-bills/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Cities

GET/v1/accounting/billy-cities/:id

Get one of cities by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-cities/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-cities/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-cities/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-cities?pageSize=100

List cities. Reference data: cities. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. cities.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-cities?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-cities?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-cities?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-cities

Create in cities. Send the record fields directly (the relay wraps them in Billy's city root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-cities' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-cities', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-cities');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-cities/:id

Update one of cities by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-cities/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-cities/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-cities/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Companies (labels)

GET/v1/accounting/billy-companies

List the Billy companies configured for this customer. A Billy access token is tied to ONE organisation, so each company is a credential row billy:<label> {access_token, organization_id, account_name}; this confirms each token against Billy (GET /v2/organizations), fills in organization_id/account_name on rows saved without them, and returns {companies:[{label, name, organization_id, ok, error?}], account_required, has_legacy_row} — labels only, never tokens. Pass a label as the account selector on every other Billy tool; account_required is true when more than one company is configured. This tool takes no account selector.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-companies' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-companies', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-companies');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Contact balance payments

GET/v1/accounting/billy-contact-balance-payments/:id

Get one of contactBalancePayments by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-contact-balance-payments?pageSize=100

List contactBalancePayments. Payments applied to a contact balance (customer/supplier account). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. contactBalancePayments.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-balance-payments?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-contact-balance-payments

Create in contactBalancePayments. Send the record fields directly (the relay wraps them in Billy's contactBalancePayment root key and adds organizationId). Required: entryDate, amount, associations.

Minimal body: {"entryDate":"2026-09-15","amount":500,"associations":[{"subjectReference":"invoice:<invoice-id>"}]}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entryDate": "2026-09-15",
    "amount": 500,
    "associations": [
        {
            "subjectReference": "invoice:<invoice-id>"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-contact-balance-payments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entryDate": "2026-09-15",
    "amount": 500,
    "associations": [
        {
            "subjectReference": "invoice:<invoice-id>"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entryDate": "2026-09-15",
      "amount": 500,
      "associations": [
          {
              "subjectReference": "invoice:<invoice-id>"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entryDate\": \"2026-09-15\",\n    \"amount\": 500,\n    \"associations\": [\n        {\n            \"subjectReference\": \"invoice:<invoice-id>\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-contact-balance-payments/:id

Update one of contactBalancePayments by {id} (partial: only the fields sent change).

Minimal body: {"entryDate":"2026-09-15"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entryDate": "2026-09-15"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entryDate": "2026-09-15"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entryDate": "2026-09-15"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entryDate\": \"2026-09-15\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Contact balance postings

GET/v1/accounting/billy-contact-balance-postings/:id

Get one of contactBalancePostings by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-balance-postings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-postings/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-postings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-contact-balance-postings?pageSize=100

List contactBalancePostings. Read-only postings on contact balances. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. contactBalancePostings.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-balance-postings?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-balance-postings?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-balance-postings?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Contact persons

GET/v1/accounting/billy-contact-persons/:id

Get one of contactPersons by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-persons/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-contact-persons?pageSize=100

List contactPersons. People at a contact; the contactPersonId an invoice email is sent to. Either name or email must be set. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. contactPersons.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contact-persons?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-persons?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-persons?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-contact-persons

Create in contactPersons. Send the record fields directly (the relay wraps them in Billy's contactPerson root key and adds organizationId). Required: contactId, name or email.

Minimal body: {"contactId":"<contact-id>","name":"Jane Doe","email":"jane@example.com"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contactId": "<contact-id>",
    "name": "Jane Doe",
    "email": "jane@example.com"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-contact-persons' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contactId": "<contact-id>",
    "name": "Jane Doe",
    "email": "jane@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-persons', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contactId": "<contact-id>",
      "name": "Jane Doe",
      "email": "jane@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-persons');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contactId\": \"<contact-id>\",\n    \"name\": \"Jane Doe\",\n    \"email\": \"jane@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-contact-persons/:id

Update one of contactPersons by {id} (partial: only the fields sent change).

Minimal body: {"contactId":"<contact-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contactId": "<contact-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-contact-persons/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contactId": "<contact-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contactId": "<contact-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contactId\": \"<contact-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-contact-persons/:id

Delete one of contactPersons by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-contact-persons/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contact-persons/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Contacts

GET/v1/accounting/billy-contacts/:id

Get one of contacts by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contacts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-contacts?pageSize=100

List contacts. Customers and suppliers (isCustomer / isSupplier; type company|person). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. contacts.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-contacts?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contacts?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contacts?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-contacts

Create in contacts. Send the record fields directly (the relay wraps them in Billy's contact root key and adds organizationId). Required: name, countryId, paymentTermsDays.

Minimal body: {"name":"Example ApS","countryId":"DK","isCustomer":true,"paymentTermsDays":8,"registrationNo":"12345678"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS",
    "countryId": "DK",
    "isCustomer": true,
    "paymentTermsDays": 8,
    "registrationNo": "12345678"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-contacts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS",
    "countryId": "DK",
    "isCustomer": true,
    "paymentTermsDays": 8,
    "registrationNo": "12345678"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS",
      "countryId": "DK",
      "isCustomer": true,
      "paymentTermsDays": 8,
      "registrationNo": "12345678"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contacts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\",\n    \"countryId\": \"DK\",\n    \"isCustomer\": true,\n    \"paymentTermsDays\": 8,\n    \"registrationNo\": \"12345678\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-contacts/:id

Update one of contacts by {id} (partial: only the fields sent change).

Minimal body: {"name":"Example ApS"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-contacts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contacts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-contacts/:id

Delete one of contacts by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-contacts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Countries

GET/v1/accounting/billy-countries/:id

Get one of countries by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-countries/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-countries/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-countries/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-countries?pageSize=100

List countries. Reference data: countries (id is the ISO code, e.g. DK). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. countries.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-countries?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-countries?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-countries?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-countries

Create in countries. Send the record fields directly (the relay wraps them in Billy's country root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-countries' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-countries', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-countries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-countries/:id

Update one of countries by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-countries/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-countries/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-countries/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Country groups

GET/v1/accounting/billy-country-groups/:id

Get one of countryGroups by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-country-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-country-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-country-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-country-groups?pageSize=100

List countryGroups. Reference data: country groups (EU, non-EU…). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. countryGroups.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-country-groups?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-country-groups?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-country-groups?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-country-groups

Create in countryGroups. Send the record fields directly (the relay wraps them in Billy's countryGroup root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-country-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-country-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-country-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-country-groups/:id

Update one of countryGroups by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-country-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-country-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-country-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Currencies

GET/v1/accounting/billy-currencies/:id

Get one of currencies by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-currencies/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-currencies/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-currencies/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-currencies?pageSize=100

List currencies. Reference data: currencies (id is the ISO code, e.g. DKK). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. currencies.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-currencies?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-currencies?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-currencies?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-currencies

Create in currencies. Send the record fields directly (the relay wraps them in Billy's currency root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-currencies' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-currencies', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-currencies');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-currencies/:id

Update one of currencies by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-currencies/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-currencies/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-currencies/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Daybook balance accounts

GET/v1/accounting/billy-daybook-balance-accounts/:id

Get one of daybookBalanceAccounts by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-daybook-balance-accounts?pageSize=100

List daybookBalanceAccounts. Balance accounts attached to a daybook. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. daybookBalanceAccounts.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-daybook-balance-accounts

Create in daybookBalanceAccounts. Send the record fields directly (the relay wraps them in Billy's daybookBalanceAccount root key and adds organizationId). Required: daybookId, accountId, priority.

Minimal body: {"daybookId":"<daybook-id>","accountId":"<account-id>","priority":1}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "daybookId": "<daybook-id>",
    "accountId": "<account-id>",
    "priority": 1
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "daybookId": "<daybook-id>",
    "accountId": "<account-id>",
    "priority": 1
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "daybookId": "<daybook-id>",
      "accountId": "<account-id>",
      "priority": 1
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"daybookId\": \"<daybook-id>\",\n    \"accountId\": \"<account-id>\",\n    \"priority\": 1\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-daybook-balance-accounts/:id

Update one of daybookBalanceAccounts by {id} (partial: only the fields sent change).

Minimal body: {"daybookId":"<daybook-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "daybookId": "<daybook-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "daybookId": "<daybook-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "daybookId": "<daybook-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"daybookId\": \"<daybook-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-daybook-balance-accounts/:id

Delete one of daybookBalanceAccounts by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-balance-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Daybook transaction lines

GET/v1/accounting/billy-daybook-transaction-lines/:id

Get one of daybookTransactionLines by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-daybook-transaction-lines?pageSize=100

List daybookTransactionLines. Lines of a daybook transaction (side debit|credit, immutable once created). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. daybookTransactionLines.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-daybook-transaction-lines

Create in daybookTransactionLines. Send the record fields directly (the relay wraps them in Billy's daybookTransactionLine root key and adds organizationId). Required: daybookTransactionId, accountId, amount, side.

Minimal body: {"daybookTransactionId":"<transaction-id>","accountId":"<account-id>","amount":100,"side":"debit","text":"Correction"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "daybookTransactionId": "<transaction-id>",
    "accountId": "<account-id>",
    "amount": 100,
    "side": "debit",
    "text": "Correction"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "daybookTransactionId": "<transaction-id>",
    "accountId": "<account-id>",
    "amount": 100,
    "side": "debit",
    "text": "Correction"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "daybookTransactionId": "<transaction-id>",
      "accountId": "<account-id>",
      "amount": 100,
      "side": "debit",
      "text": "Correction"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"daybookTransactionId\": \"<transaction-id>\",\n    \"accountId\": \"<account-id>\",\n    \"amount\": 100,\n    \"side\": \"debit\",\n    \"text\": \"Correction\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-daybook-transaction-lines/:id

Update one of daybookTransactionLines by {id} (partial: only the fields sent change).

Minimal body: {"daybookTransactionId":"<transaction-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "daybookTransactionId": "<transaction-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "daybookTransactionId": "<transaction-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "daybookTransactionId": "<transaction-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"daybookTransactionId\": \"<transaction-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-daybook-transaction-lines/:id

Delete one of daybookTransactionLines by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transaction-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Daybook transactions (manual entries)

GET/v1/accounting/billy-daybook-transactions/:id

Get one daybook transaction by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-daybook-transactions?daybookId=%3Cdaybook-id%3E&state=draft

List daybook transactions (documented filters allow-listed: daybookId, apiType, state, minEntryDate, maxEntryDate, q, sortProperty, sortDirection, include, page, pageSize).

Minimal query: {"daybookId":"<daybook-id>","state":"draft"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
daybookId<daybook-id>
statedraft
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybook-transactions?daybookId=%3Cdaybook-id%3E&state=draft' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transactions?daybookId=%3Cdaybook-id%3E&state=draft', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transactions?daybookId=%3Cdaybook-id%3E&state=draft');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-daybook-transactions

Create a manual journal entry as a DRAFT transaction in a daybook: lines embedded, at least one, each {accountId, amount, side debit|credit, text?}. Via action: approve (id) books it. Required: daybookId, entryDate, lines.

Minimal body: {"daybookId":"<daybook-id>","entryDate":"2026-09-30","lines":[{"accountId":"<expense-account-id>","amount":100,"side":"debit","text":"Correction"},{"accountId":"<bank-account-id>","amount":100,"side":"credit","text":"Correction"}]}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "daybookId": "<daybook-id>",
    "entryDate": "2026-09-30",
    "lines": [
        {
            "accountId": "<expense-account-id>",
            "amount": 100,
            "side": "debit",
            "text": "Correction"
        },
        {
            "accountId": "<bank-account-id>",
            "amount": 100,
            "side": "credit",
            "text": "Correction"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-daybook-transactions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "daybookId": "<daybook-id>",
    "entryDate": "2026-09-30",
    "lines": [
        {
            "accountId": "<expense-account-id>",
            "amount": 100,
            "side": "debit",
            "text": "Correction"
        },
        {
            "accountId": "<bank-account-id>",
            "amount": 100,
            "side": "credit",
            "text": "Correction"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transactions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "daybookId": "<daybook-id>",
      "entryDate": "2026-09-30",
      "lines": [
          {
              "accountId": "<expense-account-id>",
              "amount": 100,
              "side": "debit",
              "text": "Correction"
          },
          {
              "accountId": "<bank-account-id>",
              "amount": 100,
              "side": "credit",
              "text": "Correction"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transactions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"daybookId\": \"<daybook-id>\",\n    \"entryDate\": \"2026-09-30\",\n    \"lines\": [\n        {\n            \"accountId\": \"<expense-account-id>\",\n            \"amount\": 100,\n            \"side\": \"debit\",\n            \"text\": \"Correction\"\n        },\n        {\n            \"accountId\": \"<bank-account-id>\",\n            \"amount\": 100,\n            \"side\": \"credit\",\n            \"text\": \"Correction\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-daybook-transactions/:id

Update a draft transaction by {id} (partial).

Minimal body: {"entryDate":"2026-09-30"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entryDate": "2026-09-30"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entryDate": "2026-09-30"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entryDate": "2026-09-30"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entryDate\": \"2026-09-30\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-daybook-transactions/:id

Delete a draft transaction by {id} (idempotent).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybook-transactions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Daybooks

GET/v1/accounting/billy-daybooks/:id

Get one of daybooks by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybooks/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-daybooks?pageSize=100

List daybooks. Journals (kassekladder) that daybook transactions post through. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. daybooks.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-daybooks?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybooks?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybooks?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-daybooks

Create in daybooks. Send the record fields directly (the relay wraps them in Billy's daybook root key and adds organizationId). Required: name, isTransactionSummaryEnabled.

Minimal body: {"name":"Manual entries","isTransactionSummaryEnabled":false}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Manual entries",
    "isTransactionSummaryEnabled": false
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-daybooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Manual entries",
    "isTransactionSummaryEnabled": false
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Manual entries",
      "isTransactionSummaryEnabled": false
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Manual entries\",\n    \"isTransactionSummaryEnabled\": false\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-daybooks/:id

Update one of daybooks by {id} (partial: only the fields sent change).

Minimal body: {"name":"Manual entries"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Manual entries"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-daybooks/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Manual entries"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybooks/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Manual entries"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Manual entries\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-daybooks/:id

Delete one of daybooks by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-daybooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-daybooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-daybooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Files (uploads)

GET/v1/accounting/billy-files/:id

Get one file by {id} (its downloadUrl is a pre-signed link).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-files/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-files/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-files/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-files?pageSize=50

List uploaded files (page, pageSize, sortProperty, sortDirection, q). action=download (id) returns the file bytes as base64.

Minimal query: {"pageSize":50}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize50
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-files?pageSize=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-files?pageSize=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-files?pageSize=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-files

Upload a file: file_name, content_base64, optional mime (guessed from the extension: pdf/png/jpg), create_attachment (default true — Billy also returns an attachment record to link to a bill or invoice via attachmentIds), create_variants (default true). Binary upload; the relay does the multipart-free binary POST Billy expects.

Required body: file_name, content_base64.

Minimal body: {"file_name":"receipt.pdf","content_base64":"<base64>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "file_name": "receipt.pdf",
    "mime": "application/pdf",
    "content_base64": "<base64>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-files' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "file_name": "receipt.pdf",
    "mime": "application/pdf",
    "content_base64": "<base64>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-files', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "file_name": "receipt.pdf",
      "mime": "application/pdf",
      "content_base64": "<base64>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-files');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"file_name\": \"receipt.pdf\",\n    \"mime\": \"application/pdf\",\n    \"content_base64\": \"<base64>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Invoice late fees

GET/v1/accounting/billy-invoice-late-fees/:id

Get one of invoiceLateFees by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-invoice-late-fees?pageSize=100

List invoiceLateFees. Late fees charged on an invoice. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. invoiceLateFees.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-late-fees?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-invoice-late-fees

Create in invoiceLateFees. Send the record fields directly (the relay wraps them in Billy's invoiceLateFee root key and adds organizationId). Required: invoiceId, entryDate, flatFee, percentageFee.

Minimal body: {"invoiceId":"<invoice-id>","entryDate":"2026-09-30","flatFee":100,"percentageFee":0}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "invoiceId": "<invoice-id>",
    "entryDate": "2026-09-30",
    "flatFee": 100,
    "percentageFee": 0
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-invoice-late-fees' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "invoiceId": "<invoice-id>",
    "entryDate": "2026-09-30",
    "flatFee": 100,
    "percentageFee": 0
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "invoiceId": "<invoice-id>",
      "entryDate": "2026-09-30",
      "flatFee": 100,
      "percentageFee": 0
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"invoiceId\": \"<invoice-id>\",\n    \"entryDate\": \"2026-09-30\",\n    \"flatFee\": 100,\n    \"percentageFee\": 0\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-invoice-late-fees/:id

Update one of invoiceLateFees by {id} (partial: only the fields sent change).

Minimal body: {"invoiceId":"<invoice-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "invoiceId": "<invoice-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "invoiceId": "<invoice-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "invoiceId": "<invoice-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-late-fees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"invoiceId\": \"<invoice-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Invoice lines

GET/v1/accounting/billy-invoice-lines/:id

Get one of invoiceLines by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-invoice-lines?pageSize=100

List invoiceLines. Lines of an invoice (creating one returns the recalculated invoice too). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. invoiceLines.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-lines?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-lines?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-lines?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-invoice-lines

Create in invoiceLines. Send the record fields directly (the relay wraps them in Billy's invoiceLine root key and adds organizationId). Required: invoiceId, productId, unitPrice.

Minimal body: {"invoiceId":"<invoice-id>","productId":"<product-id>","unitPrice":800,"quantity":1,"description":"Hosting september"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "invoiceId": "<invoice-id>",
    "productId": "<product-id>",
    "unitPrice": 800,
    "quantity": 1,
    "description": "Hosting september"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-invoice-lines' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "invoiceId": "<invoice-id>",
    "productId": "<product-id>",
    "unitPrice": 800,
    "quantity": 1,
    "description": "Hosting september"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-lines', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "invoiceId": "<invoice-id>",
      "productId": "<product-id>",
      "unitPrice": 800,
      "quantity": 1,
      "description": "Hosting september"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-lines');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"invoiceId\": \"<invoice-id>\",\n    \"productId\": \"<product-id>\",\n    \"unitPrice\": 800,\n    \"quantity\": 1,\n    \"description\": \"Hosting september\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-invoice-lines/:id

Update one of invoiceLines by {id} (partial: only the fields sent change).

Minimal body: {"invoiceId":"<invoice-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "invoiceId": "<invoice-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "invoiceId": "<invoice-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "invoiceId": "<invoice-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"invoiceId\": \"<invoice-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-invoice-lines/:id

Delete one of invoiceLines by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-lines/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Invoice reminder associations

GET/v1/accounting/billy-invoice-reminder-associations/:id

Get one of invoiceReminderAssociations by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-invoice-reminder-associations?pageSize=100

List invoiceReminderAssociations. Which invoices a reminder covers. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. invoiceReminderAssociations.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-invoice-reminder-associations

Create in invoiceReminderAssociations. Send the record fields directly (the relay wraps them in Billy's invoiceReminderAssociation root key and adds organizationId). Required: reminderId, invoiceId.

Minimal body: {"reminderId":"<reminder-id>","invoiceId":"<invoice-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "reminderId": "<reminder-id>",
    "invoiceId": "<invoice-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "reminderId": "<reminder-id>",
    "invoiceId": "<invoice-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "reminderId": "<reminder-id>",
      "invoiceId": "<invoice-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"reminderId\": \"<reminder-id>\",\n    \"invoiceId\": \"<invoice-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-invoice-reminder-associations/:id

Update one of invoiceReminderAssociations by {id} (partial: only the fields sent change).

Minimal body: {"reminderId":"<reminder-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "reminderId": "<reminder-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "reminderId": "<reminder-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "reminderId": "<reminder-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"reminderId\": \"<reminder-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-invoice-reminder-associations/:id

Delete one of invoiceReminderAssociations by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminder-associations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Invoice reminders

GET/v1/accounting/billy-invoice-reminders/:id

Get one of invoiceReminders by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-reminders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminders/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-invoice-reminders?pageSize=100

List invoiceReminders. Payment reminders (rykkere) sent to a contact person. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. invoiceReminders.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoice-reminders?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminders?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminders?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-invoice-reminders

Create in invoiceReminders. Send the record fields directly (the relay wraps them in Billy's invoiceReminder root key and adds organizationId). Required: contactId, feeCurrencyId, contactPersonId, emailSubject, emailBody.

Minimal body: {"contactId":"<contact-id>","contactPersonId":"<person-id>","feeCurrencyId":"DKK","emailSubject":"Reminder","emailBody":"Your invoice is overdue."}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contactId": "<contact-id>",
    "contactPersonId": "<person-id>",
    "feeCurrencyId": "DKK",
    "emailSubject": "Reminder",
    "emailBody": "Your invoice is overdue."
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-invoice-reminders' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contactId": "<contact-id>",
    "contactPersonId": "<person-id>",
    "feeCurrencyId": "DKK",
    "emailSubject": "Reminder",
    "emailBody": "Your invoice is overdue."
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoice-reminders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contactId": "<contact-id>",
      "contactPersonId": "<person-id>",
      "feeCurrencyId": "DKK",
      "emailSubject": "Reminder",
      "emailBody": "Your invoice is overdue."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoice-reminders');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contactId\": \"<contact-id>\",\n    \"contactPersonId\": \"<person-id>\",\n    \"feeCurrencyId\": \"DKK\",\n    \"emailSubject\": \"Reminder\",\n    \"emailBody\": \"Your invoice is overdue.\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Invoices

GET/v1/accounting/billy-invoices/:id

Get one invoice by {id} (add include=invoice.lines:embed over HTTP to embed lines).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-invoices?state=approved&entryDatePeriod=month%3A2026-09&pageSize=100

List invoices (documented filters allow-listed: contactId, creditedInvoiceId, state draft|approved|voided, invoiceNo, externalId, minEntryDate, maxEntryDate, entryDatePeriod, minApprovedTime, maxApprovedTime, approvedTimePeriod, minDueDate, maxDueDate, isPaid, currencyId, recurringInvoiceId, amount, quoteId, q, sortProperty, sortDirection, include, page, pageSize). Read sub-operations via action: pdf (id) returns the invoice PDF as base64 from its downloadUrl; logs (id) returns the e-invoice delivery log (received/signedoff = delivered, failed = error).

Minimal query: {"state":"approved","entryDatePeriod":"month:2026-09","pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
stateapproved
entryDatePeriodmonth:2026-09
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-invoices?state=approved&entryDatePeriod=month%3A2026-09&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoices?state=approved&entryDatePeriod=month%3A2026-09&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoices?state=approved&entryDatePeriod=month%3A2026-09&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-invoices

Create a DRAFT invoice (send the record fields; the relay wraps them in Billy's invoice root key and adds organizationId; lines may be embedded, each {productId, unitPrice, quantity, description}). Write sub-operations via action: approve (id) books the draft — the only state change Billy allows; email (id, contactPersonId, emailSubject, emailBody[, copyToUserId]) sends it; credit_note (id of the invoice to credit; other fields forwarded) creates a credit note; deliver (id[, receiverIdType gln|cvr, receiverGln, orderReference, senderUserId]) sends it as an e-invoice (must be approved first; poll action=logs). Required on create: contactId, entryDate, lines.

Minimal body: {"contactId":"<contact-id>","entryDate":"2026-09-01","paymentTermsDays":8,"currencyId":"DKK","lines":[{"productId":"<product-id>","unitPrice":800,"quantity":1,"description":"Hosting september"}]}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contactId": "<contact-id>",
    "entryDate": "2026-09-01",
    "paymentTermsDays": 8,
    "currencyId": "DKK",
    "lines": [
        {
            "productId": "<product-id>",
            "unitPrice": 800,
            "quantity": 1,
            "description": "Hosting september"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-invoices' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contactId": "<contact-id>",
    "entryDate": "2026-09-01",
    "paymentTermsDays": 8,
    "currencyId": "DKK",
    "lines": [
        {
            "productId": "<product-id>",
            "unitPrice": 800,
            "quantity": 1,
            "description": "Hosting september"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contactId": "<contact-id>",
      "entryDate": "2026-09-01",
      "paymentTermsDays": 8,
      "currencyId": "DKK",
      "lines": [
          {
              "productId": "<product-id>",
              "unitPrice": 800,
              "quantity": 1,
              "description": "Hosting september"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoices');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contactId\": \"<contact-id>\",\n    \"entryDate\": \"2026-09-01\",\n    \"paymentTermsDays\": 8,\n    \"currencyId\": \"DKK\",\n    \"lines\": [\n        {\n            \"productId\": \"<product-id>\",\n            \"unitPrice\": 800,\n            \"quantity\": 1,\n            \"description\": \"Hosting september\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-invoices/:id

Update a draft invoice by {id} (partial: only the fields sent change). Approved invoices cannot be edited.

Minimal body: {"paymentTermsDays":14}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "paymentTermsDays": 14
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-invoices/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "paymentTermsDays": 14
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoices/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "paymentTermsDays": 14
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"paymentTermsDays\": 14\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-invoices/:id

Delete a draft invoice by {id} (idempotent).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-invoices/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Locales

GET/v1/accounting/billy-locales/:id

Get one of locales by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-locales/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-locales/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-locales/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-locales?pageSize=100

List locales. Reference data: locales. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. locales.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-locales?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-locales?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-locales?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-locales

Create in locales. Send the record fields directly (the relay wraps them in Billy's locale root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-locales' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-locales', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-locales');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-locales/:id

Update one of locales by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-locales/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-locales/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-locales/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Organizations

GET/v1/accounting/billy-organizations/:id

Get one of organizations by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-organizations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-organizations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-organizations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-organizations?pageSize=100

List organizations. The organisation (company) behind the token: settings, fiscal year, VAT period, invoice numbering. list returns the one the token sees. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. organizations.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-organizations?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-organizations?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-organizations?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-organizations

Create in organizations. Send the record fields directly (the relay wraps them in Billy's organization root key and adds organizationId). Required: name, ownerUserId, countryId, baseCurrencyId.

Minimal body: {"name":"Example ApS"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-organizations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-organizations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-organizations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-organizations/:id

Update one of organizations by {id} (partial: only the fields sent change).

Minimal body: {"name":"Example ApS"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-organizations/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-organizations/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-organizations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Postings

GET/v1/accounting/billy-postings/:id

Get one of postings by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-postings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-postings/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-postings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-postings?pageSize=100

List postings. The ledger: every posting on every account. Filters accountId, transactionId, entryDate, minEntryDate, maxEntryDate, entryDatePeriod (year:2026, month:2026-09, quarter:2026-3, dates:from...to), isVoided, isBankMatched, isNotZero, ids, q. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. postings.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-postings?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-postings?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-postings?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Product prices

GET/v1/accounting/billy-product-prices/:id

Get one of productPrices by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-product-prices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-product-prices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-product-prices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-product-prices?pageSize=100

List productPrices. A product's price in a currency. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. productPrices.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-product-prices?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-product-prices?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-product-prices?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-product-prices

Create in productPrices. Send the record fields directly (the relay wraps them in Billy's productPrice root key and adds organizationId). Required: productId, unitPrice, currencyId.

Minimal body: {"productId":"<product-id>","unitPrice":800,"currencyId":"DKK"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productId": "<product-id>",
    "unitPrice": 800,
    "currencyId": "DKK"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-product-prices' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productId": "<product-id>",
    "unitPrice": 800,
    "currencyId": "DKK"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-product-prices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productId": "<product-id>",
      "unitPrice": 800,
      "currencyId": "DKK"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-product-prices');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productId\": \"<product-id>\",\n    \"unitPrice\": 800,\n    \"currencyId\": \"DKK\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-product-prices/:id

Update one of productPrices by {id} (partial: only the fields sent change).

Minimal body: {"productId":"<product-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productId": "<product-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-product-prices/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productId": "<product-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-product-prices/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productId": "<product-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-product-prices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productId\": \"<product-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-product-prices/:id

Delete one of productPrices by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-product-prices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-product-prices/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-product-prices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Products

GET/v1/accounting/billy-products/:id

Get one of products by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-products/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-products?pageSize=100

List products. Products and services sold on invoices (prices live in productPrices). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. products.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-products?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-products?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-products?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-products

Create in products. Send the record fields directly (the relay wraps them in Billy's product root key and adds organizationId). Required: name, accountId.

Minimal body: {"name":"Hosting","accountId":"<sales-account-id>","productNo":"HOST-1"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting",
    "accountId": "<sales-account-id>",
    "productNo": "HOST-1"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-products' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting",
    "accountId": "<sales-account-id>",
    "productNo": "HOST-1"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting",
      "accountId": "<sales-account-id>",
      "productNo": "HOST-1"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-products');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting\",\n    \"accountId\": \"<sales-account-id>\",\n    \"productNo\": \"HOST-1\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-products/:id

Update one of products by {id} (partial: only the fields sent change).

Minimal body: {"name":"Hosting"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-products/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-products/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-products/:id

Delete one of products by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-products/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax accounts

GET/v1/accounting/billy-sales-tax-accounts/:id

Get one of salesTaxAccounts by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-accounts?pageSize=100

List salesTaxAccounts. VAT accounts by type. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxAccounts.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-sales-tax-accounts

Create in salesTaxAccounts. Send the record fields directly (the relay wraps them in Billy's salesTaxAccount root key and adds organizationId). Required: accountId, type.

Minimal body: {"accountId":"<account-id>","type":"salesTax"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<account-id>",
    "type": "salesTax"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<account-id>",
    "type": "salesTax"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<account-id>",
      "type": "salesTax"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<account-id>\",\n    \"type\": \"salesTax\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-accounts/:id

Update one of salesTaxAccounts by {id} (partial: only the fields sent change).

Minimal body: {"accountId":"<account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountId": "<account-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountId": "<account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountId": "<account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountId\": \"<account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-sales-tax-accounts/:id

Delete one of salesTaxAccounts by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax meta fields

GET/v1/accounting/billy-sales-tax-meta-fields/:id

Get one of salesTaxMetaFields by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-meta-fields?pageSize=100

List salesTaxMetaFields. Custom VAT-return fields. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxMetaFields.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-sales-tax-meta-fields

Create in salesTaxMetaFields. Send the record fields directly (the relay wraps them in Billy's salesTaxMetaField root key and adds organizationId). Required: name.

Minimal body: {"name":"Rubrik A"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Rubrik A"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Rubrik A"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Rubrik A"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Rubrik A\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-meta-fields/:id

Update one of salesTaxMetaFields by {id} (partial: only the fields sent change).

Minimal body: {"name":"Rubrik A"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Rubrik A"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Rubrik A"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Rubrik A"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Rubrik A\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-sales-tax-meta-fields/:id

Delete one of salesTaxMetaFields by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-meta-fields/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax payments

GET/v1/accounting/billy-sales-tax-payments/:id

Get one of salesTaxPayments by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-payments?pageSize=100

List salesTaxPayments. Payments of a VAT return. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxPayments.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-payments?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-sales-tax-payments

Create in salesTaxPayments. Send the record fields directly (the relay wraps them in Billy's salesTaxPayment root key and adds organizationId). Required: salesTaxReturnId, entryDate, accountId.

Minimal body: {"salesTaxReturnId":"<return-id>","entryDate":"2026-09-01","accountId":"<bank-account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "salesTaxReturnId": "<return-id>",
    "entryDate": "2026-09-01",
    "accountId": "<bank-account-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-sales-tax-payments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "salesTaxReturnId": "<return-id>",
    "entryDate": "2026-09-01",
    "accountId": "<bank-account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "salesTaxReturnId": "<return-id>",
      "entryDate": "2026-09-01",
      "accountId": "<bank-account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"salesTaxReturnId\": \"<return-id>\",\n    \"entryDate\": \"2026-09-01\",\n    \"accountId\": \"<bank-account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-payments/:id

Update one of salesTaxPayments by {id} (partial: only the fields sent change).

Minimal body: {"salesTaxReturnId":"<return-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "salesTaxReturnId": "<return-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "salesTaxReturnId": "<return-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "salesTaxReturnId": "<return-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-payments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"salesTaxReturnId\": \"<return-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax returns

GET/v1/accounting/billy-sales-tax-returns/:id

Get one of salesTaxReturns by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-returns?pageSize=100

List salesTaxReturns. VAT returns (momsangivelser); Billy creates them, you read and update them. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxReturns.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-returns?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-returns/:id

Update one of salesTaxReturns by {id} (partial: only the fields sent change).

Minimal body: {"isPaid":true}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "isPaid": true
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "isPaid": true
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "isPaid": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-returns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"isPaid\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax rules

GET/v1/accounting/billy-sales-tax-rules/:id

Get one of salesTaxRules by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-rules?pageSize=100

List salesTaxRules. Per-country rules inside a ruleset. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxRules.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rules?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-sales-tax-rules

Create in salesTaxRules. Send the record fields directly (the relay wraps them in Billy's salesTaxRule root key and adds organizationId). Required: rulesetId, countryId.

Minimal body: {"rulesetId":"<ruleset-id>","countryId":"DE"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "rulesetId": "<ruleset-id>",
    "countryId": "DE"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rules' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "rulesetId": "<ruleset-id>",
    "countryId": "DE"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "rulesetId": "<ruleset-id>",
      "countryId": "DE"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"rulesetId\": \"<ruleset-id>\",\n    \"countryId\": \"DE\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-rules/:id

Update one of salesTaxRules by {id} (partial: only the fields sent change).

Minimal body: {"rulesetId":"<ruleset-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "rulesetId": "<ruleset-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "rulesetId": "<ruleset-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "rulesetId": "<ruleset-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"rulesetId\": \"<ruleset-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-sales-tax-rules/:id

Delete one of salesTaxRules by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Sales tax rulesets

GET/v1/accounting/billy-sales-tax-rulesets/:id

Get one of salesTaxRulesets by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-sales-tax-rulesets?pageSize=100

List salesTaxRulesets. Named sets of VAT rules. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. salesTaxRulesets.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-sales-tax-rulesets

Create in salesTaxRulesets. Send the record fields directly (the relay wraps them in Billy's salesTaxRuleset root key and adds organizationId). Required: name.

Minimal body: {"name":"EU services"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "EU services"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "EU services"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "EU services"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"EU services\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-sales-tax-rulesets/:id

Update one of salesTaxRulesets by {id} (partial: only the fields sent change).

Minimal body: {"name":"EU services"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "EU services"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "EU services"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "EU services"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"EU services\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-sales-tax-rulesets/:id

Delete one of salesTaxRulesets by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-sales-tax-rulesets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — States

GET/v1/accounting/billy-states/:id

Get one of states by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-states/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-states/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-states/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-states?pageSize=100

List states. Reference data: states/regions. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. states.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-states?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-states?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-states?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-states

Create in states. Send the record fields directly (the relay wraps them in Billy's state root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-states' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-states', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-states');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-states/:id

Update one of states by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-states/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-states/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-states/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Tax rate deduction components

GET/v1/accounting/billy-tax-rate-deduction-components/:id

Get one of taxRateDeductionComponents by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-tax-rate-deduction-components?pageSize=100

List taxRateDeductionComponents. Deduction components of a tax rate (partial VAT deduction). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. taxRateDeductionComponents.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-tax-rate-deduction-components

Create in taxRateDeductionComponents. Send the record fields directly (the relay wraps them in Billy's taxRateDeductionComponent root key and adds organizationId). Required: taxRateId, share, source, accountId.

Minimal body: {"taxRateId":"<tax-rate-id>","share":0.5,"source":"tax","accountId":"<account-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "taxRateId": "<tax-rate-id>",
    "share": 0.5,
    "source": "tax",
    "accountId": "<account-id>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "taxRateId": "<tax-rate-id>",
    "share": 0.5,
    "source": "tax",
    "accountId": "<account-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "taxRateId": "<tax-rate-id>",
      "share": 0.5,
      "source": "tax",
      "accountId": "<account-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"taxRateId\": \"<tax-rate-id>\",\n    \"share\": 0.5,\n    \"source\": \"tax\",\n    \"accountId\": \"<account-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-tax-rate-deduction-components/:id

Update one of taxRateDeductionComponents by {id} (partial: only the fields sent change).

Minimal body: {"taxRateId":"<tax-rate-id>"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "taxRateId": "<tax-rate-id>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "taxRateId": "<tax-rate-id>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "taxRateId": "<tax-rate-id>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"taxRateId\": \"<tax-rate-id>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-tax-rate-deduction-components/:id

Delete one of taxRateDeductionComponents by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rate-deduction-components/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Tax rates

GET/v1/accounting/billy-tax-rates/:id

Get one of taxRates by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-tax-rates/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-tax-rates?pageSize=100

List taxRates. VAT rates (predefinedTag such as p_dk_25 on a predefined chart; custom rates are not available to every organisation). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. taxRates.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-tax-rates?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rates?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rates?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-tax-rates

Create in taxRates. Send the record fields directly (the relay wraps them in Billy's taxRate root key and adds organizationId). Required: name, rate.

Minimal body: {"name":"Moms 25%","rate":0.25,"predefinedTag":"p_dk_25"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Moms 25%",
    "rate": 0.25,
    "predefinedTag": "p_dk_25"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-tax-rates' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Moms 25%",
    "rate": 0.25,
    "predefinedTag": "p_dk_25"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rates', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Moms 25%",
      "rate": 0.25,
      "predefinedTag": "p_dk_25"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rates');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Moms 25%\",\n    \"rate\": 0.25,\n    \"predefinedTag\": \"p_dk_25\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-tax-rates/:id

Update one of taxRates by {id} (partial: only the fields sent change).

Minimal body: {"name":"Moms 25%"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Moms 25%"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-tax-rates/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Moms 25%"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Moms 25%"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Moms 25%\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/billy-tax-rates/:id

Delete one of taxRates by {id} (idempotent; Billy answers 200 even if already gone).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/billy-tax-rates/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-tax-rates/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Transactions

GET/v1/accounting/billy-transactions/:id

Get one of transactions by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-transactions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-transactions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-transactions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-transactions?pageSize=100

List transactions. Read-only booked transactions (the postings' parents). Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. transactions.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-transactions?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-transactions?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-transactions?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Users

GET/v1/accounting/billy-users/:id

Get one user by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-users/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-users/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-users/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-users?action=me

List the organisation's users, or action=me for the user behind the token (GET /v2/user).

Minimal query: {"action":"me"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
actionme
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-users?action=me' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-users?action=me', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-users?action=me');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-users/:id

Update a user by {id} (name, email).

Minimal body: {"name":"Jane Doe"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Jane Doe"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-users/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Jane Doe"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-users/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Jane Doe"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-users/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Jane Doe\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Billy — Zipcodes

GET/v1/accounting/billy-zipcodes/:id

Get one of zipcodes by {id}.

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-zipcodes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-zipcodes/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-zipcodes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/billy-zipcodes?pageSize=100

List zipcodes. Reference data: zip codes. Common params: page, pageSize (max 1000), sortProperty, sortDirection, include (sideload, e.g. zipcodes.contact:embed), q.

Minimal query: {"pageSize":100}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

AuthorizationBearer YOUR_JWT_TOKEN
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/billy-zipcodes?pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-zipcodes?pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-zipcodes?pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/billy-zipcodes

Create in zipcodes. Send the record fields directly (the relay wraps them in Billy's zipcode root key and adds organizationId).

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/billy-zipcodes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-zipcodes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-zipcodes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/billy-zipcodes/:id

Update one of zipcodes by {id} (partial: only the fields sent change).

Minimal body: {"name":"Renamed"}

_Requires stored credentials: billy (PUT /v1/credentials/billy)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/billy-zipcodes/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/billy-zipcodes/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/billy-zipcodes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Accounting years

GET/v1/accounting/dinero-accounting-years

List accounting years. action=possible_end_dates returns valid end dates for a new year.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-accounting-years' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-accounting-years', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-accounting-years');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-accounting-years

Create an accounting year. action=possible_end_dates computes candidate end dates instead.

Minimal body: {"StartDate":"2026-01-01","EndDate":"2026-12-31"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "StartDate": "2026-01-01",
    "EndDate": "2026-12-31"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-accounting-years' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "StartDate": "2026-01-01",
    "EndDate": "2026-12-31"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-accounting-years', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "StartDate": "2026-01-01",
      "EndDate": "2026-12-31"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-accounting-years');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"StartDate\": \"2026-01-01\",\n    \"EndDate\": \"2026-12-31\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Attachments

GET/v1/accounting/dinero-attachments/:id

List the attachments bound to a document {documentGuid}. The guid is the Guid of an invoice/voucher/credit-note — get it from that resource's own list tool (invoices, vouchers, purchase-vouchers, sales-credit-notes).

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-attachments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-attachments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-attachments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-attachments

action: bind (document_guid, document_type — bind the default file), add_file (document_guid, file_guid, file_name), delete_file (document_guid, file_guid).

Required body: document_guid.

Minimal body: {"document_guid":"<doc-guid>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "add_file",
    "document_guid": "<doc-guid>",
    "file_guid": "<file-guid>",
    "file_name": "receipt.pdf"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-attachments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "add_file",
    "document_guid": "<doc-guid>",
    "file_guid": "<file-guid>",
    "file_name": "receipt.pdf"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-attachments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "add_file",
      "document_guid": "<doc-guid>",
      "file_guid": "<file-guid>",
      "file_name": "receipt.pdf"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-attachments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"add_file\",\n    \"document_guid\": \"<doc-guid>\",\n    \"file_guid\": \"<file-guid>\",\n    \"file_name\": \"receipt.pdf\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Business goals

GET/v1/accounting/dinero-business-goals?year_key=%3Caccounting-year-key%3E

Get business goals for an accounting year (year_key). action: performance (yearly performance), monthly (monthly goals).

Required query: year_key.

Minimal query: {"year_key":"<accounting-year-key>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
year_key<accounting-year-key>
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-business-goals?year_key=%3Caccounting-year-key%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-business-goals?year_key=%3Caccounting-year-key%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-business-goals?year_key=%3Caccounting-year-key%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-business-goals

Set yearly business goals for an accounting year (year_key).

Required body: year_key.

Minimal body: {"year_key":"<accounting-year-key>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "year_key": "<accounting-year-key>",
    "RevenueGoal": 1000000
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-business-goals' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "year_key": "<accounting-year-key>",
    "RevenueGoal": 1000000
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-business-goals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "year_key": "<accounting-year-key>",
      "RevenueGoal": 1000000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-business-goals');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"year_key\": \"<accounting-year-key>\",\n    \"RevenueGoal\": 1000000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Chart of accounts

GET/v1/accounting/dinero-accounts?kind=entry

List chart-of-accounts entries. kind = entry (default) | purchase | deposit; for deposit add internal=1 for internal deposit accounts. Forwards fields, categoryFilter.

Minimal query: {"kind":"entry"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
kindentry
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-accounts?kind=entry' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-accounts?kind=entry', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-accounts?kind=entry');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-accounts

Create an account. action = entry (default) | deposit.

Minimal body: {"action":"entry","Name":"New revenue account","AccountNumber":1010}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "entry",
    "Name": "New revenue account",
    "AccountNumber": 1010
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "entry",
    "Name": "New revenue account",
    "AccountNumber": 1010
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "entry",
      "Name": "New revenue account",
      "AccountNumber": 1010
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"entry\",\n    \"Name\": \"New revenue account\",\n    \"AccountNumber\": 1010\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Companies (labels)

GET/v1/accounting/dinero-companies

List the Dinero companies configured for this customer — the organisations ticked in the admin "Connect Dinero" flow, stored as dinero:<label> pointer rows. Returns {companies:[{label, name, organization_id}], account_required:bool, has_legacy_org:bool} — never secrets. Pass one of these labels as the account selector on the other Dinero tools; with several companies configured the selector is required. This tool takes no account selector.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-companies' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-companies', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-companies');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Contacts

GET/v1/accounting/dinero-contacts/:id

Get a single contact by its {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-contacts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-contacts?page=0&pageSize=100

List contacts (forwards fields, queryFilter, changesSince, page, pageSize). action=notes lists/gets a contact's notes (contact_guid [+note_guid]). api_version=v2 uses the v2 contacts list.

Minimal query: {"page":0,"pageSize":100}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-contacts?page=0&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-contacts?page=0&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-contacts?page=0&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-contacts

Create a contact. action=restore undeletes (id). action=note adds a note (contact_guid). action=note_delete removes a note (contact_guid,note_guid).

Minimal body: {"Name":"Acme ApS","CountryKey":"DK","Email":"billing@acme.dk","IsPerson":false,"IsMember":false,"UseCvr":false}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Acme ApS",
    "CountryKey": "DK",
    "Email": "billing@acme.dk",
    "IsPerson": false,
    "IsMember": false,
    "UseCvr": false
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-contacts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Acme ApS",
    "CountryKey": "DK",
    "Email": "billing@acme.dk",
    "IsPerson": false,
    "IsMember": false,
    "UseCvr": false
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Acme ApS",
      "CountryKey": "DK",
      "Email": "billing@acme.dk",
      "IsPerson": false,
      "IsMember": false,
      "UseCvr": false
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-contacts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Acme ApS\",\n    \"CountryKey\": \"DK\",\n    \"Email\": \"billing@acme.dk\",\n    \"IsPerson\": false,\n    \"IsMember\": false,\n    \"UseCvr\": false\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-contacts/:id

Update a contact by {guid} (api_version=v2 for v2). action=note updates a note (contact_guid, {guid}=note guid).

Minimal body: {"Name":"Acme ApS","Email":"ap@acme.dk"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Acme ApS",
    "Email": "ap@acme.dk"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-contacts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Acme ApS",
    "Email": "ap@acme.dk"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-contacts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Acme ApS",
      "Email": "ap@acme.dk"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Acme ApS\",\n    \"Email\": \"ap@acme.dk\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-contacts/:id

Delete a contact by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-contacts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Countries

GET/v1/accounting/dinero-countries

List supported countries (global; not organisation-scoped).

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-countries' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-countries', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-countries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Custom reports (expenses per company)

GET/v1/accounting/dinero-custom-reports

List the report definitions for the company (account): the built-in expenses plus any saved for that organisation. Each carries its definition: include / exclude account ranges, include_types (which English types count as spend), account_types (account or range → type), category_types (Dinero category → type), keywords (type → extra substrings).

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-custom-reports' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-custom-reports', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-custom-reports');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-custom-reports?report=expenses&from=2026-08-01&to=2026-08-31&detail=summary

Run a server-side spend report for one company (pick it with account). Every account row carries an ENGLISH type — income, financial_income, cost_of_goods, expense, salary, depreciation, financial_cost, tax, vat, bank, receivable, payable, asset, liability, equity, other — plus side (result | balance), Dinero's own category (e.g. Salary, Administration Expenses, Vat Due — fetched with fields, it is the authoritative account type), and type_source (definition | dinero_category | category | standard_chart | name | unmapped). Read type, never guess from the account name. report = a definition slug (built-in expenses; saved ones via the list tool). from/to = YYYY-MM-DD (default: current month to date). detail = summary (default) | detailed (adds the ledger lines per account: date, voucher, description, amount). group_by=month forces a per-month breakdown (added automatically when the period spans months). Returns totals {expenses, salary, expenses_excl_salary, by_type, entries, accounts}, by_type, by_category (with type and shares), by_account (sorted by total, is_salary flagged), optional by_month (with by_type), excluded (every active account left out, with reason), unmapped (accounts no rule could type — map them), and legend (one line per type present). Spend = the types in include_types (default cost_of_goods, expense, salary, depreciation, financial_cost) unless the definition names include ranges. action=chart returns the whole chart of accounts classified the same way, without entries — read it once before trusting a report. One-off overrides: include, exclude, salary (account ranges like 2000-2999,3100), include_types (comma list); save them with the create tool to make them stick. notes states the sign convention (debit positive; credit notes reduce spend).

Minimal query: {"report":"expenses","from":"2026-08-01","to":"2026-08-31","detail":"summary"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
reportexpenses
from2026-08-01
to2026-08-31
detailsummary
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-custom-reports?report=expenses&from=2026-08-01&to=2026-08-31&detail=summary' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-custom-reports?report=expenses&from=2026-08-01&to=2026-08-31&detail=summary', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-custom-reports?report=expenses&from=2026-08-01&to=2026-08-31&detail=summary');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-custom-reports

Save (or replace) a report definition for the company (account). slug (lowercase, e.g. expenses to override the built-in, or a new one like marketing), name, and definition — an object with any of: include / exclude (account ranges, "2000-2999,3100" or arrays), include_types (list of English types that count as spend), account_types ({"3610": "salary", "2200-2299": "salary"} — the definition's word on specific accounts), salary (shorthand: ranges typed salary), category_types ({"Lønninger": "salary"} — Dinero category → type), keywords ({"expense": ["leasing"]} — extra substrings per type). Types: income, financial_income, cost_of_goods, expense, salary, depreciation, financial_cost, tax, vat, bank, receivable, payable, asset, liability, equity, other. Keys you omit keep the built-in defaults. The keys may also be given inline instead of under definition.

Minimal body: {"slug":"expenses","name":"Expenses (excl. owner salary)","definition":{"exclude":"2250","account_types":{"2200-2299":"salary"},"include_types":["cost_of_goods","expense","salary","financial_cost"]}}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "slug": "expenses",
    "name": "Expenses (excl. owner salary)",
    "definition": {
        "exclude": "2250",
        "account_types": {
            "2200-2299": "salary"
        },
        "include_types": [
            "cost_of_goods",
            "expense",
            "salary",
            "financial_cost"
        ]
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-custom-reports' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "slug": "expenses",
    "name": "Expenses (excl. owner salary)",
    "definition": {
        "exclude": "2250",
        "account_types": {
            "2200-2299": "salary"
        },
        "include_types": [
            "cost_of_goods",
            "expense",
            "salary",
            "financial_cost"
        ]
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-custom-reports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "slug": "expenses",
      "name": "Expenses (excl. owner salary)",
      "definition": {
          "exclude": "2250",
          "account_types": {
              "2200-2299": "salary"
          },
          "include_types": [
              "cost_of_goods",
              "expense",
              "salary",
              "financial_cost"
          ]
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-custom-reports');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"slug\": \"expenses\",\n    \"name\": \"Expenses (excl. owner salary)\",\n    \"definition\": {\n        \"exclude\": \"2250\",\n        \"account_types\": {\n            \"2200-2299\": \"salary\"\n        },\n        \"include_types\": [\n            \"cost_of_goods\",\n            \"expense\",\n            \"salary\",\n            \"financial_cost\"\n        ]\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-custom-reports/:id

Update a saved definition by {slug} (or override the built-in): name and/or definition keys are merged onto the existing ones.

Minimal body: {"definition":{"account_types":{"3610":"salary"}}}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "definition": {
        "account_types": {
            "3610": "salary"
        }
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "definition": {
        "account_types": {
            "3610": "salary"
        }
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "definition": {
          "account_types": {
              "3610": "salary"
          }
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"definition\": {\n        \"account_types\": {\n            \"3610\": \"salary\"\n        }\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-custom-reports/:id

Delete a saved definition by {slug} for the company. Deleting expenses removes the company's override and restores the built-in.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-custom-reports/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Electronic invoice

GET/v1/accounting/dinero-electronic-invoice?ean=5790000000000

Validate an e-invoice recipient (forwards the recipient identifiers, e.g. ean/cvr).

Minimal query: {"ean":"5790000000000"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
ean5790000000000
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-electronic-invoice?ean=5790000000000' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-electronic-invoice?ean=5790000000000', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-electronic-invoice?ean=5790000000000');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Entries (general ledger)

GET/v1/accounting/dinero-entries?fromDate=2026-01-01&toDate=2026-12-31

List accounting entries (forwards fromDate, toDate, accountNumber, includePrimo). action=changes returns entries changed since a cursor.

Minimal query: {"fromDate":"2026-01-01","toDate":"2026-12-31"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
fromDate2026-01-01
toDate2026-12-31
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-entries?fromDate=2026-01-01&toDate=2026-12-31' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-entries?fromDate=2026-01-01&toDate=2026-12-31', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-entries?fromDate=2026-01-01&toDate=2026-12-31');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Files

GET/v1/accounting/dinero-files/:id

Get file metadata by {fileGuid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-files/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-files/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-files/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-files

List uploaded files available for attaching.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-files' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-files', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-files');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-files

Register/upload a file. action=attachment creates it as an attachment.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-files' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-files', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-files');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Integrations

GET/v1/accounting/dinero-integrations

List integrations. action: mobilepay_access (MobilePay accounting access), pensopay (PensoPay status).

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-integrations' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-integrations', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-integrations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-integrations

Integration lifecycle. action: mobilepay_start (mobilepay_version v1|v2), mobilepay_activate, mobilepay_deactivate, pensopay_start, pensopay_reactivate, pensopay_deactivate, pensopay_mobilepay (PUT).

Required body: action.

Minimal body: {"action":"mobilepay_start"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "mobilepay_start"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-integrations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "mobilepay_start"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-integrations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "mobilepay_start"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-integrations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"mobilepay_start\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Invoices

GET/v1/accounting/dinero-invoices/:id

Get a single invoice by its {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-invoices?page=0&pageSize=100&statusFilter=Booked

List invoices, or a read sub-operation via action: pdf (id), payments (id), mailouts (id), templates, validate_trustpilot (contact_guid), email_template (id), pre_reminder_template (id), reminders (voucher_guid [+reminder_id|next]), reminder_template (voucher_guid). Without action, lists invoices (forwards Dinero filters such as page, pageSize, statusFilter, startDate, endDate, freeTextSearch, changesSince).

Minimal query: {"page":0,"pageSize":100,"statusFilter":"Booked"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
statusFilterBooked
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-invoices?page=0&pageSize=100&statusFilter=Booked' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-invoices?page=0&pageSize=100&statusFilter=Booked', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-invoices?page=0&pageSize=100&statusFilter=Booked');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-invoices

Create a draft invoice, or a write sub-operation via action: book (id), fetch, email (id), pre_reminder (id), e_invoice (id), add_payment (id), generate_creditnote (id); reminders: reminder_create/reminder_fetch/reminder_email (voucher_guid), reminder_update/reminder_delete/reminder_book/reminder_e_reminder (voucher_guid + reminder_id). Fields other than action/id/voucher_guid/reminder_id are forwarded to Dinero verbatim.

Minimal body: {"ContactGuid":"<contact-guid>","Currency":"DKK","Language":"da-DK","Date":"2026-01-15","ProductLines":[{"BaseAmountValue":1000,"Quantity":1,"AccountNumber":1000,"Unit":"parts","Description":"Consulting","LineType":"Product"}]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Language": "da-DK",
    "Date": "2026-01-15",
    "ProductLines": [
        {
            "BaseAmountValue": 1000,
            "Quantity": 1,
            "AccountNumber": 1000,
            "Unit": "parts",
            "Description": "Consulting",
            "LineType": "Product"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-invoices' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Language": "da-DK",
    "Date": "2026-01-15",
    "ProductLines": [
        {
            "BaseAmountValue": 1000,
            "Quantity": 1,
            "AccountNumber": 1000,
            "Unit": "parts",
            "Description": "Consulting",
            "LineType": "Product"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ContactGuid": "<contact-guid>",
      "Currency": "DKK",
      "Language": "da-DK",
      "Date": "2026-01-15",
      "ProductLines": [
          {
              "BaseAmountValue": 1000,
              "Quantity": 1,
              "AccountNumber": 1000,
              "Unit": "parts",
              "Description": "Consulting",
              "LineType": "Product"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-invoices');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ContactGuid\": \"<contact-guid>\",\n    \"Currency\": \"DKK\",\n    \"Language\": \"da-DK\",\n    \"Date\": \"2026-01-15\",\n    \"ProductLines\": [\n        {\n            \"BaseAmountValue\": 1000,\n            \"Quantity\": 1,\n            \"AccountNumber\": 1000,\n            \"Unit\": \"parts\",\n            \"Description\": \"Consulting\",\n            \"LineType\": \"Product\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-invoices/:id

Update a draft invoice (PUT v1.2). Include the current Timestamp for optimistic concurrency.

Minimal body: {"Timestamp":"<timestamp>","ProductLines":[]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Timestamp": "<timestamp>",
    "ProductLines": []
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-invoices/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Timestamp": "<timestamp>",
    "ProductLines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-invoices/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Timestamp": "<timestamp>",
      "ProductLines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Timestamp\": \"<timestamp>\",\n    \"ProductLines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-invoices/:id

Delete a draft invoice by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-invoices/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Ledger items

POST/v1/accounting/dinero-ledger-items

Create ledger items (POST v1.2). action: ledgers (create ledgers), update (PUT — payload identifies the line, no id segment), book, status, delete.

Minimal body: [{"AccountNumber":1000,"Amount":1000,"BalancingAccountNumber":55000,"Description":"Manual posting","VoucherDate":"2026-01-15"}]

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[
    {
        "AccountNumber": 1000,
        "Amount": 1000,
        "BalancingAccountNumber": 55000,
        "Description": "Manual posting",
        "VoucherDate": "2026-01-15"
    }
]
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-ledger-items' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[
    {
        "AccountNumber": 1000,
        "Amount": 1000,
        "BalancingAccountNumber": 55000,
        "Description": "Manual posting",
        "VoucherDate": "2026-01-15"
    }
]'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-ledger-items', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([
      {
          "AccountNumber": 1000,
          "Amount": 1000,
          "BalancingAccountNumber": 55000,
          "Description": "Manual posting",
          "VoucherDate": "2026-01-15"
      }
  ])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-ledger-items');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "[\n    {\n        \"AccountNumber\": 1000,\n        \"Amount\": 1000,\n        \"BalancingAccountNumber\": 55000,\n        \"Description\": \"Manual posting\",\n        \"VoucherDate\": \"2026-01-15\"\n    }\n]");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Organizations

GET/v1/accounting/dinero-organizations

List organisations the token can access (global). api_version=v1.1 for the newer list. action=is_verified returns the current org's verification status.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-organizations' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-organizations', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-organizations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-organizations

Create an organisation (global). action: update / delete (current org), delete_verification (remove org verification).

Minimal body: {"Name":"My Company ApS","VatNumber":"12345678","CountryKey":"DK"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "My Company ApS",
    "VatNumber": "12345678",
    "CountryKey": "DK"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-organizations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "My Company ApS",
    "VatNumber": "12345678",
    "CountryKey": "DK"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-organizations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "My Company ApS",
      "VatNumber": "12345678",
      "CountryKey": "DK"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-organizations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"My Company ApS\",\n    \"VatNumber\": \"12345678\",\n    \"CountryKey\": \"DK\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Products

GET/v1/accounting/dinero-products/:id

Get a single product by its {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-products/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-products?page=0&pageSize=100

List products (forwards fields, freeTextSearch, queryFilter, changesSince, page, pageSize).

Minimal query: {"page":0,"pageSize":100}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-products?page=0&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-products?page=0&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-products?page=0&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-products

Create a product.

Minimal body: {"Name":"Consulting hour","BaseAmountValue":1000,"AccountNumber":1000,"Unit":"hours","Quantity":1}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Consulting hour",
    "BaseAmountValue": 1000,
    "AccountNumber": 1000,
    "Unit": "hours",
    "Quantity": 1
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-products' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Consulting hour",
    "BaseAmountValue": 1000,
    "AccountNumber": 1000,
    "Unit": "hours",
    "Quantity": 1
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Consulting hour",
      "BaseAmountValue": 1000,
      "AccountNumber": 1000,
      "Unit": "hours",
      "Quantity": 1
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-products');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Consulting hour\",\n    \"BaseAmountValue\": 1000,\n    \"AccountNumber\": 1000,\n    \"Unit\": \"hours\",\n    \"Quantity\": 1\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-products/:id

Update a product by {guid}.

Minimal body: {"Name":"Consulting hour","BaseAmountValue":1200}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Consulting hour",
    "BaseAmountValue": 1200
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-products/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Consulting hour",
    "BaseAmountValue": 1200
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-products/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Consulting hour",
      "BaseAmountValue": 1200
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Consulting hour\",\n    \"BaseAmountValue\": 1200\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-products/:id

Delete a product by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-products/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Purchase voucher payments

GET/v1/accounting/dinero-purchase-vouchers?id=%3Cpurchase-voucher-id%3E

List a purchase voucher's payments (id required). action=v2 uses the v2 payments view.

Required query: id.

Minimal query: {"id":"<purchase-voucher-id>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
id<purchase-voucher-id>
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers?id=%3Cpurchase-voucher-id%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers?id=%3Cpurchase-voucher-id%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers?id=%3Cpurchase-voucher-id%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-purchase-vouchers

Add a payment to a purchase voucher (id). action=calculations previews a payment; action=delete_payment removes one (id, payment_id, timestamp).

Required body: id.

Minimal body: {"id":"<purchase-voucher-id>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "id": "<purchase-voucher-id>",
    "Amount": 1250,
    "PaymentDate": "2026-01-20",
    "DepositAccountNumber": 55000
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "id": "<purchase-voucher-id>",
    "Amount": 1250,
    "PaymentDate": "2026-01-20",
    "DepositAccountNumber": 55000
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "id": "<purchase-voucher-id>",
      "Amount": 1250,
      "PaymentDate": "2026-01-20",
      "DepositAccountNumber": 55000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-purchase-vouchers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"id\": \"<purchase-voucher-id>\",\n    \"Amount\": 1250,\n    \"PaymentDate\": \"2026-01-20\",\n    \"DepositAccountNumber\": 55000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Reports

GET/v1/accounting/dinero-reports?report=result&year=2026

Financial reports for an accounting year. report = saldo | result | primo | balance; year = accounting-year key. Extra params (date ranges) are forwarded.

Required query: report, year.

Minimal query: {"report":"result","year":"2026"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
reportresult
year2026
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-reports?report=result&year=2026' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-reports?report=result&year=2026', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-reports?report=result&year=2026');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — SMS

POST/v1/accounting/dinero-sms

action=validate checks SMS settings; otherwise sends a voucher SMS (voucher_guid required).

Required body: voucher_guid.

Minimal body: {"voucher_guid":"<voucher-guid>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "voucher_guid": "<voucher-guid>",
    "PhoneNumber": "+4512345678"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-sms' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "voucher_guid": "<voucher-guid>",
    "PhoneNumber": "+4512345678"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sms', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "voucher_guid": "<voucher-guid>",
      "PhoneNumber": "+4512345678"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sms');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"voucher_guid\": \"<voucher-guid>\",\n    \"PhoneNumber\": \"+4512345678\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Sales credit notes

GET/v1/accounting/dinero-sales-credit-notes/:id

Get a single credit note by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-sales-credit-notes?page=0&pageSize=100

List credit notes. action: sales (sales list), settings (sales settings), pdf (id), payments (id), mailouts (id), email_template (id).

Minimal query: {"page":0,"pageSize":100}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes?page=0&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes?page=0&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes?page=0&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-sales-credit-notes

Create a credit note. action: book (id), email (id), e_creditnote (id), fetch, add_payment (id), delete_payment (id, payment_guid).

Minimal body: {"ContactGuid":"<contact-guid>","Currency":"DKK","Date":"2026-01-15","ProductLines":[{"BaseAmountValue":-500,"Quantity":1,"AccountNumber":1000,"Unit":"parts","Description":"Refund"}]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Date": "2026-01-15",
    "ProductLines": [
        {
            "BaseAmountValue": -500,
            "Quantity": 1,
            "AccountNumber": 1000,
            "Unit": "parts",
            "Description": "Refund"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Date": "2026-01-15",
    "ProductLines": [
        {
            "BaseAmountValue": -500,
            "Quantity": 1,
            "AccountNumber": 1000,
            "Unit": "parts",
            "Description": "Refund"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ContactGuid": "<contact-guid>",
      "Currency": "DKK",
      "Date": "2026-01-15",
      "ProductLines": [
          {
              "BaseAmountValue": -500,
              "Quantity": 1,
              "AccountNumber": 1000,
              "Unit": "parts",
              "Description": "Refund"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ContactGuid\": \"<contact-guid>\",\n    \"Currency\": \"DKK\",\n    \"Date\": \"2026-01-15\",\n    \"ProductLines\": [\n        {\n            \"BaseAmountValue\": -500,\n            \"Quantity\": 1,\n            \"AccountNumber\": 1000,\n            \"Unit\": \"parts\",\n            \"Description\": \"Refund\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-sales-credit-notes/:id

Update a credit note by {guid} (PUT v1.2).

Minimal body: {"Timestamp":"<timestamp>","ProductLines":[]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Timestamp": "<timestamp>",
    "ProductLines": []
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Timestamp": "<timestamp>",
    "ProductLines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Timestamp": "<timestamp>",
      "ProductLines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Timestamp\": \"<timestamp>\",\n    \"ProductLines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-sales-credit-notes/:id

Delete a credit note by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-sales-credit-notes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Settings

POST/v1/accounting/dinero-settings

Update organisation access settings (PUT settings/access upstream).

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-settings' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-settings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-settings');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — State of account

GET/v1/accounting/dinero-state-of-account/:id

Get the statement data for a contact {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-state-of-account/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-state-of-account/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-state-of-account/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-state-of-account?id=%3Ccontact-guid%3E

Get a contact's statement of account. action=pdf (with id) returns the PDF (base64).

Required query: id.

Minimal query: {"id":"<contact-guid>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
id<contact-guid>
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-state-of-account?id=%3Ccontact-guid%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-state-of-account?id=%3Ccontact-guid%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-state-of-account?id=%3Ccontact-guid%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-state-of-account

Email a contact's statement of account (id = contact guid).

Required body: id.

Minimal body: {"id":"<contact-guid>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "id": "<contact-guid>",
    "Receiver": "ap@acme.dk"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-state-of-account' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "id": "<contact-guid>",
    "Receiver": "ap@acme.dk"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-state-of-account', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "id": "<contact-guid>",
      "Receiver": "ap@acme.dk"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-state-of-account');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"id\": \"<contact-guid>\",\n    \"Receiver\": \"ap@acme.dk\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Trade offers (quotes)

GET/v1/accounting/dinero-trade-offers/:id

Get a single trade offer by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/dinero-trade-offers?page=0&pageSize=100

List trade offers. action: email_template (id, email_version v1|v2 default v2), mailouts (id).

Minimal query: {"page":0,"pageSize":100}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-trade-offers?page=0&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-trade-offers?page=0&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-trade-offers?page=0&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-trade-offers

Create a trade offer. action: email (id, email_version), generate_invoice (id), fetch.

Minimal body: {"ContactGuid":"<contact-guid>","Currency":"DKK","Date":"2026-01-15","ProductLines":[]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Date": "2026-01-15",
    "ProductLines": []
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-trade-offers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ContactGuid": "<contact-guid>",
    "Currency": "DKK",
    "Date": "2026-01-15",
    "ProductLines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-trade-offers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ContactGuid": "<contact-guid>",
      "Currency": "DKK",
      "Date": "2026-01-15",
      "ProductLines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-trade-offers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ContactGuid\": \"<contact-guid>\",\n    \"Currency\": \"DKK\",\n    \"Date\": \"2026-01-15\",\n    \"ProductLines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/dinero-trade-offers/:id

Update a trade offer by {guid} (PUT v1.2).

Minimal body: {"ProductLines":[]}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ProductLines": []
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ProductLines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ProductLines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ProductLines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/dinero-trade-offers/:id

Delete a trade offer by {guid}.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-trade-offers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Unified vouchers

GET/v1/accounting/dinero-unified-vouchers?page=0&pageSize=100

List vouchers across types in a unified view (forwards filters/paging).

Minimal query: {"page":0,"pageSize":100}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
pageSize100
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-unified-vouchers?page=0&pageSize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-unified-vouchers?page=0&pageSize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-unified-vouchers?page=0&pageSize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — VAT types

GET/v1/accounting/dinero-vat-types

List the organisation's VAT types.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-vat-types' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-vat-types', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-vat-types');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Voucher templates

GET/v1/accounting/dinero-voucher-templates?action=types

action: types (default — list template types), get (template_type, id), deposits (template_type, voucher_id).

Minimal query: {"action":"types"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
actiontypes
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-voucher-templates?action=types' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-voucher-templates?action=types', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-voucher-templates?action=types');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-voucher-templates

Create a template (year_key, template_type). action: update (year_key, template_type, id — PUT), book (template_type, id), similar_exists (year_key, template_type).

Required body: year_key, template_type.

Minimal body: {"year_key":"<accounting-year-key>","template_type":"manuel"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "year_key": "<accounting-year-key>",
    "template_type": "manuel"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-voucher-templates' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "year_key": "<accounting-year-key>",
    "template_type": "manuel"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-voucher-templates', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "year_key": "<accounting-year-key>",
      "template_type": "manuel"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-voucher-templates');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"year_key\": \"<accounting-year-key>\",\n    \"template_type\": \"manuel\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Vouchers (manual / purchase)

GET/v1/accounting/dinero-vouchers?type=purchase&id=%3Cguid%3E

Read a voucher. Required type = manual | purchase | purchase_creditnote. Pass id (guid), or for purchase file_guid to look up by attached file.

Required query: type, id.

Minimal query: {"type":"purchase","id":"<guid>"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
typepurchase
id<guid>
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-vouchers?type=purchase&id=%3Cguid%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-vouchers?type=purchase&id=%3Cguid%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-vouchers?type=purchase&id=%3Cguid%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-vouchers

Create a voucher. Required type = manual | purchase | purchase_creditnote. action: book/update/delete (all types), and for purchase: fetch, similar, generate_creditnote. version overrides the create/update API version (purchase create defaults v1.2, update v1.1).

Required body: type.

Minimal body: {"type":"purchase"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "type": "purchase",
    "ContactGuid": "<supplier-guid>",
    "Date": "2026-01-15",
    "Lines": []
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-vouchers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "type": "purchase",
    "ContactGuid": "<supplier-guid>",
    "Date": "2026-01-15",
    "Lines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-vouchers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "type": "purchase",
      "ContactGuid": "<supplier-guid>",
      "Date": "2026-01-15",
      "Lines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-vouchers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"type\": \"purchase\",\n    \"ContactGuid\": \"<supplier-guid>\",\n    \"Date\": \"2026-01-15\",\n    \"Lines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Dinero — Webhooks

GET/v1/accounting/dinero-webhooks

List webhook subscriptions. action=events lists subscribable event types.

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/dinero-webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/dinero-webhooks

Subscribe to a webhook event. action=unsubscribe removes a subscription.

Minimal body: {"Event":"CreateSalesInvoice","CallbackUrl":"https://example.com/hook"}

_Requires stored credentials: dinero (PUT /v1/credentials/dinero)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Event": "CreateSalesInvoice",
    "CallbackUrl": "https://example.com/hook"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/dinero-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Event": "CreateSalesInvoice",
    "CallbackUrl": "https://example.com/hook"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/dinero-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Event": "CreateSalesInvoice",
      "CallbackUrl": "https://example.com/hook"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/dinero-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Event\": \"CreateSalesInvoice\",\n    \"CallbackUrl\": \"https://example.com/hook\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Bank statements

GET/v1/accounting/uniconta-bank-statements/:id

Get one BankStatementClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-bank-statements?take=100

Query BankStatementClient (POST /Query/Get). Bank statements (BankStatementClient) and their lines (BankStatementLineClient via the entities tool); settle them with the journals tool (action settle_bank). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-bank-statements?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-bank-statements?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-bank-statements?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-bank-statements

Insert a BankStatementClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=BankStatementClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Name":"Main bank"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Main bank"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-bank-statements' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Main bank"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-bank-statements', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Main bank"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-bank-statements');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Main bank\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-bank-statements/:id

Update a BankStatementClient by {RowId}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-bank-statements/:id

Delete a BankStatementClient by {RowId} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-bank-statements/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Companies (labels)

GET/v1/accounting/uniconta-companies

List the Uniconta companies configured for this customer: every uniconta:<label> row {company_id, account_name}, each confirmed by binding a session to it (ChangeCompany; fills in account_name from CompanyName), plus available — the companies the Server-Login user can reach (Query/Get/CompanyClient on a login-only session, best effort) so you know which company_id to store. Returns {companies:[{label, name, company_id, currency, ok, error?}], account_required, has_legacy_row, login_configured, available, available_error} — never the login or a token. Pass a label as the account selector on every other Uniconta tool. This tool takes no selector.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-companies' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-companies', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-companies');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Contacts

GET/v1/accounting/uniconta-contacts/:id

Get one ContactClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-contacts?take=100

Query ContactClient (POST /Query/Get). Contact persons on debtors, creditors and prospects (ContactClient). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-contacts?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-contacts?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-contacts?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-contacts

Insert a ContactClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=ContactClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Name":"Jane Doe","Email":"jane@example.com","DCAccount":"C1001"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Jane Doe",
    "Email": "jane@example.com",
    "DCAccount": "C1001"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-contacts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Jane Doe",
    "Email": "jane@example.com",
    "DCAccount": "C1001"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Jane Doe",
      "Email": "jane@example.com",
      "DCAccount": "C1001"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-contacts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Jane Doe\",\n    \"Email\": \"jane@example.com\",\n    \"DCAccount\": \"C1001\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-contacts/:id

Update a ContactClient by {RowId}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Email":"jane@example.com"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Email": "jane@example.com"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-contacts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Email": "jane@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Email": "jane@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Email\": \"jane@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-contacts/:id

Delete a ContactClient by {RowId} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Creditors (suppliers)

GET/v1/accounting/uniconta-creditors/:id

Get one CreditorClient by {Account} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-creditors/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-creditors?take=100

Query CreditorClient (POST /Query/Get). Suppliers (CreditorClient): the same shape as debtors on the purchase side. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-creditors?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-creditors?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-creditors?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-creditors

Insert a CreditorClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=CreditorClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Account":"S2001","Name":"Hosting Co","Group":"1","Currency":"DKK"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Account": "S2001",
    "Name": "Hosting Co",
    "Group": "1",
    "Currency": "DKK"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-creditors' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Account": "S2001",
    "Name": "Hosting Co",
    "Group": "1",
    "Currency": "DKK"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-creditors', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Account": "S2001",
      "Name": "Hosting Co",
      "Group": "1",
      "Currency": "DKK"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-creditors');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Account\": \"S2001\",\n    \"Name\": \"Hosting Co\",\n    \"Group\": \"1\",\n    \"Currency\": \"DKK\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-creditors/:id

Update a CreditorClient by {Account}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Hosting Co"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Hosting Co"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-creditors/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Hosting Co"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Hosting Co"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Hosting Co\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-creditors/:id

Delete a CreditorClient by {Account} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-creditors/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-creditors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Debtors (customers)

GET/v1/accounting/uniconta-debtors/:id

Get one DebtorClient by {Account} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-debtors/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-debtors?take=100

Query DebtorClient (POST /Query/Get). Customers (DebtorClient): Account, Name, Address1-3, ZipCode, City, Country, CompanyRegNo, VatNumber, Currency, Payment, Group, Vat, VatZone, PriceGroup, Blocked, ContactEmail, InvoiceEmail, EAN, Dimension1-5. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-debtors?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-debtors?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-debtors?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-debtors

Insert a DebtorClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=DebtorClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Account":"C1001","Name":"Example ApS","Group":"1","Country":"Denmark","Currency":"DKK","Payment":"8D","CompanyRegNo":"12345678"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Account": "C1001",
    "Name": "Example ApS",
    "Group": "1",
    "Country": "Denmark",
    "Currency": "DKK",
    "Payment": "8D",
    "CompanyRegNo": "12345678"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-debtors' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Account": "C1001",
    "Name": "Example ApS",
    "Group": "1",
    "Country": "Denmark",
    "Currency": "DKK",
    "Payment": "8D",
    "CompanyRegNo": "12345678"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-debtors', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Account": "C1001",
      "Name": "Example ApS",
      "Group": "1",
      "Country": "Denmark",
      "Currency": "DKK",
      "Payment": "8D",
      "CompanyRegNo": "12345678"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-debtors');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Account\": \"C1001\",\n    \"Name\": \"Example ApS\",\n    \"Group\": \"1\",\n    \"Country\": \"Denmark\",\n    \"Currency\": \"DKK\",\n    \"Payment\": \"8D\",\n    \"CompanyRegNo\": \"12345678\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-debtors/:id

Update a DebtorClient by {Account}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Example ApS"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Example ApS"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-debtors/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-debtors/:id

Delete a DebtorClient by {Account} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-debtors/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-debtors/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Documents (attachments + voucher inbox)

GET/v1/accounting/uniconta-documents/:id

Get one document record by {RowId} (metadata; kind=voucher over HTTP for the inbox).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-documents/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-documents/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-documents/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-documents?take=100

List documents attached to records (UserDocsClient; filters TableId, TableRowId, KeyStr, Group) or, with kind=voucher, the purchase voucher inbox (VouchersClient; filters Approved, Invoice, CreditorAccount). action=content + row_id returns the file bytes as {mime, content_base64} (GET /Query/GetDocumentFromRowId).

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-documents?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-documents?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-documents?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-documents

Upload: file_name + content_base64 (+ text) and the target fields Uniconta shows for the record (TableId + TableRowId, or KeyStr/TableReference) — POST /Crud/Insert/UserDocsClient with the bytes in UserDocument. kind=voucher drops the file in the voucher inbox instead (VouchersClient: VoucherAttachment; add Amount, DocumentDate, CreditorAccount, Invoice, Text to pre-fill the booking).

Required body: file_name, content_base64.

Minimal body: {"file_name":"receipt.pdf","content_base64":"<base64>"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "voucher",
    "file_name": "receipt.pdf",
    "content_base64": "<base64>",
    "text": "Hosting september",
    "Amount": 1000
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-documents' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "voucher",
    "file_name": "receipt.pdf",
    "content_base64": "<base64>",
    "text": "Hosting september",
    "Amount": 1000
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-documents', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "voucher",
      "file_name": "receipt.pdf",
      "content_base64": "<base64>",
      "text": "Hosting september",
      "Amount": 1000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-documents');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"voucher\",\n    \"file_name\": \"receipt.pdf\",\n    \"content_base64\": \"<base64>\",\n    \"text\": \"Hosting september\",\n    \"Amount\": 1000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-documents/:id

Delete a document by {RowId}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-documents/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-documents/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-documents/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Employees

GET/v1/accounting/uniconta-employees/:id

Get one EmployeeClient by {Number} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-employees/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-employees/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-employees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-employees?take=100

Query EmployeeClient (POST /Query/Get). Employees (EmployeeClient) for projects, time and payroll categories. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-employees?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-employees?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-employees?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-employees

Insert a EmployeeClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=EmployeeClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Number":"E1","Name":"Jane Doe"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Number": "E1",
    "Name": "Jane Doe"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-employees' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Number": "E1",
    "Name": "Jane Doe"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-employees', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Number": "E1",
      "Name": "Jane Doe"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-employees');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Number\": \"E1\",\n    \"Name\": \"Jane Doe\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-employees/:id

Update a EmployeeClient by {Number}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Jane Doe"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Jane Doe"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-employees/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Jane Doe"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-employees/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Jane Doe"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-employees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Jane Doe\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-employees/:id

Delete a EmployeeClient by {Number} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-employees/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-employees/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-employees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Entities (any of the 255 tables)

GET/v1/accounting/uniconta-entities/:id

Get one record by {Entity}:{RowId} (a query on RowId).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-entities/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-entities/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-entities/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-entities?entity=DebtorClient&take=100

Query ANY Uniconta table (POST /Query/Get/{entity}): entity (e.g. DebtorClient; case-insensitive, the Client suffix is implied, typos answer with the closest names), filters = [{property, value}] using Uniconta's grammar (equality; ranges 01-01-2025..31-05-2025 in dd-MM-yyyy; lists 100;110;129; wildcards *abc*), skip, take, order_by, descending; any other key is an equality filter. action=tables lists the table names; action=fields + entity lists a table's fields and types; action=by_master + master_entity + master (record) + entity reads a master's children (PUT /Query/GetByMaster). Returns {entity, items}.

Minimal query: {"entity":"DebtorClient","take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
entityDebtorClient
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-entities?entity=DebtorClient&take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-entities?entity=DebtorClient&take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-entities?entity=DebtorClient&take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-entities

Insert: entity + record (POST /Crud/Insert), entity + records[] (POST /Crud/InsertList), or master_entity + master + child_entity + details[] (POST /Crud/InsertMasterAndDetails; with action=child_records POST /Crud/InsertChildRecords adds children to an existing master). Field names are Uniconta's own (action=fields on the query tool).

Required body: entity.

Minimal body: {"entity":"DebtorClient"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "entity": "DebtorClient",
    "record": {
        "Account": "C1001",
        "Name": "Example ApS",
        "Group": "1"
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-entities' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "entity": "DebtorClient",
    "record": {
        "Account": "C1001",
        "Name": "Example ApS",
        "Group": "1"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-entities', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "entity": "DebtorClient",
      "record": {
          "Account": "C1001",
          "Name": "Example ApS",
          "Group": "1"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-entities');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"entity\": \"DebtorClient\",\n    \"record\": {\n        \"Account\": \"C1001\",\n        \"Name\": \"Example ApS\",\n        \"Group\": \"1\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-entities/:id

Update by {Entity}:{RowId}: record (the full record, PUT /Crud/Update), fields (fetched by RowId, merged, PUT), or original + modified (PUT /Crud/UpdateFieldsModified).

Minimal body: {"fields":{"Name":"Renamed"}}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "fields": {
        "Name": "Renamed"
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-entities/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "fields": {
        "Name": "Renamed"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-entities/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "fields": {
          "Name": "Renamed"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-entities/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"fields\": {\n        \"Name\": \"Renamed\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-entities/:id

Delete by {Entity}:{RowId} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-entities/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-entities/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-entities/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Exchange rates

GET/v1/accounting/uniconta-exchange-rates/:id

Get one ExchangeRateClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-exchange-rates/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-exchange-rates/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-exchange-rates/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-exchange-rates?take=100

Query ExchangeRateClient (POST /Query/Get). Currency exchange rates (ExchangeRateClient). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-exchange-rates?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-exchange-rates?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-exchange-rates?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Finance years

GET/v1/accounting/uniconta-finance-years/:id

Get one CompanyFinanceYearClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-finance-years/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-finance-years/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-finance-years/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-finance-years?take=100

Query CompanyFinanceYearClient (POST /Query/Get). Fiscal years (CompanyFinanceYearClient): FromDate, ToDate, Current, OpenForPosting. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-finance-years?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-finance-years?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-finance-years?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — GL accounts (chart)

GET/v1/accounting/uniconta-accounts/:id

Get one GLAccountClient by {Account} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-accounts?take=100

Query GLAccountClient (POST /Query/Get). The chart of accounts (GLAccountClient): Account, Name, AccountType (PL / Balance / Header / Sum), Vat, Sum, Blocked, SystemAccount, Balance, Dimension1-5. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-accounts?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-accounts?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-accounts?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-accounts

Insert a GLAccountClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=GLAccountClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Account":"7325","Name":"Cloud hosting","AccountType":0,"Vat":"I25"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Account": "7325",
    "Name": "Cloud hosting",
    "AccountType": 0,
    "Vat": "I25"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Account": "7325",
    "Name": "Cloud hosting",
    "AccountType": 0,
    "Vat": "I25"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Account": "7325",
      "Name": "Cloud hosting",
      "AccountType": 0,
      "Vat": "I25"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Account\": \"7325\",\n    \"Name\": \"Cloud hosting\",\n    \"AccountType\": 0,\n    \"Vat\": \"I25\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-accounts/:id

Update a GLAccountClient by {Account}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Cloud hosting"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Cloud hosting"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Cloud hosting"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Cloud hosting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Cloud hosting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-accounts/:id

Delete a GLAccountClient by {Account} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — GL budget lines

GET/v1/accounting/uniconta-budgets/:id

Get one GLBudgetLineClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-budgets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-budgets?take=100

Query GLBudgetLineClient (POST /Query/Get). Budget lines (GLBudgetLineClient) per account and period, under a budget (GLBudgetClient). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-budgets?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-budgets?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-budgets?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-budgets

Insert a GLBudgetLineClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=GLBudgetLineClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Account":"7320","Date":"2026-01-01","Amount":12000}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Account": "7320",
    "Date": "2026-01-01",
    "Amount": 12000
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-budgets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Account": "7320",
    "Date": "2026-01-01",
    "Amount": 12000
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-budgets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Account": "7320",
      "Date": "2026-01-01",
      "Amount": 12000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-budgets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Account\": \"7320\",\n    \"Date\": \"2026-01-01\",\n    \"Amount\": 12000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-budgets/:id

Update a GLBudgetLineClient by {RowId}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Date":"2026-01-01"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Date": "2026-01-01"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-budgets/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Date": "2026-01-01"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Date": "2026-01-01"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Date\": \"2026-01-01\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-budgets/:id

Delete a GLBudgetLineClient by {RowId} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-budgets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — GL journals (daily journals + posting)

GET/v1/accounting/uniconta-journals/:id

Get one daily journal by {Journal}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-journals/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-journals/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-journals?take=100

List the daily journals (GLDailyJournalClient). action=lines + journal lists a journal's unposted lines (GLDailyJournalLineClient). Query params: filters, skip, take, order_by, descending.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-journals?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-journals?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-journals?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-journals

Without action: insert draft lines into a daily journal — journal + lines[] (GLDailyJournalLineClient: Date, Account, OffsetAccount, Amount debit-positive or Debit/Credit, Vat, Text, Voucher, Invoice, AccountType/OffsetAccountType Finans|Debtor|Creditor, DueDate, Project, Dimension1-5). action=check validates a posting without booking (POST /GLJournal/CheckJournal {header, lines}); action=post posts straight to the ledger (POST /GLJournal/PostJournal {header, lines, simulate}) — lines as GLPostingLineWA {Account, Amount, AccountType 0 finance|1 debtor|2 creditor, Text, Vat, Voucher, Invoice, DueDate, Project, Currency, AmountCur}, header as GLPostingHeaderWA {Date, NumberSerie, TransType, Comment, GenerateVoucher, OneVoucherForAll, FixedVoucherNumber}; action=post_daily posts a daily journal's lines (POST /GLJournal/PostDailyJournal {journal, date, comment}); action=settle_bank settles bank statement lines against ledger transactions (POST /GLJournal/SettleBankStatement {bank, settlements[] {BankLine, TransLines[]}}).

Minimal body: {"action":"post","header":{"Date":"2026-09-30","GenerateVoucher":true,"OneVoucherForAll":true,"Comment":"Correction"},"lines":[{"AccountType":0,"Account":"7320","Amount":100,"Text":"Correction"},{"AccountType":0,"Account":"55000","Amount":-100,"Text":"Correction"}]}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "post",
    "header": {
        "Date": "2026-09-30",
        "GenerateVoucher": true,
        "OneVoucherForAll": true,
        "Comment": "Correction"
    },
    "lines": [
        {
            "AccountType": 0,
            "Account": "7320",
            "Amount": 100,
            "Text": "Correction"
        },
        {
            "AccountType": 0,
            "Account": "55000",
            "Amount": -100,
            "Text": "Correction"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-journals' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "post",
    "header": {
        "Date": "2026-09-30",
        "GenerateVoucher": true,
        "OneVoucherForAll": true,
        "Comment": "Correction"
    },
    "lines": [
        {
            "AccountType": 0,
            "Account": "7320",
            "Amount": 100,
            "Text": "Correction"
        },
        {
            "AccountType": 0,
            "Account": "55000",
            "Amount": -100,
            "Text": "Correction"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-journals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "post",
      "header": {
          "Date": "2026-09-30",
          "GenerateVoucher": true,
          "OneVoucherForAll": true,
          "Comment": "Correction"
      },
      "lines": [
          {
              "AccountType": 0,
              "Account": "7320",
              "Amount": 100,
              "Text": "Correction"
          },
          {
              "AccountType": 0,
              "Account": "55000",
              "Amount": -100,
              "Text": "Correction"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-journals');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"post\",\n    \"header\": {\n        \"Date\": \"2026-09-30\",\n        \"GenerateVoucher\": true,\n        \"OneVoucherForAll\": true,\n        \"Comment\": \"Correction\"\n    },\n    \"lines\": [\n        {\n            \"AccountType\": 0,\n            \"Account\": \"7320\",\n            \"Amount\": 100,\n            \"Text\": \"Correction\"\n        },\n        {\n            \"AccountType\": 0,\n            \"Account\": \"55000\",\n            \"Amount\": -100,\n            \"Text\": \"Correction\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — GL transactions (ledger)

GET/v1/accounting/uniconta-transactions/:id

Get one GLTransClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-transactions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-transactions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-transactions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-transactions?take=100

Query GLTransClient (POST /Query/Get). Posted ledger lines (GLTransClient): Account, Date, Amount, Debit, Credit, Voucher, Text, Invoice, Vat, Dimension1-5. Filter on Account or Date for a period. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-transactions?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-transactions?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-transactions?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Inventory journals

GET/v1/accounting/uniconta-inventory-journals/:id

Get one inventory journal by {Journal}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-inventory-journals/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-inventory-journals?take=100

List inventory journals (InvJournalClient). action=lines + journal lists a journal's movement lines (InvJournalLineClient: Date, Item, Qty, MovementType, Warehouse, Location, Price, CostPrice, Text).

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-inventory-journals?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-inventory-journals

Without action: insert lines — journal + lines[] (POST /Crud/InsertList/InvJournalLineClient). action=post posts the journal (POST /InventoryJournal/PostJournal {journal, date, text, trans_type, comment, fixed_voucher, simulate, lines}).

Minimal body: {"journal":"INV","lines":[{"Date":"2026-09-21","Item":"HOST-1","Qty":10,"MovementType":"Add","Warehouse":"MAIN"}]}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "journal": "INV",
    "lines": [
        {
            "Date": "2026-09-21",
            "Item": "HOST-1",
            "Qty": 10,
            "MovementType": "Add",
            "Warehouse": "MAIN"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-inventory-journals' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "journal": "INV",
    "lines": [
        {
            "Date": "2026-09-21",
            "Item": "HOST-1",
            "Qty": 10,
            "MovementType": "Add",
            "Warehouse": "MAIN"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "journal": "INV",
      "lines": [
          {
              "Date": "2026-09-21",
              "Item": "HOST-1",
              "Qty": 10,
              "MovementType": "Add",
              "Warehouse": "MAIN"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-inventory-journals');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"journal\": \"INV\",\n    \"lines\": [\n        {\n            \"Date\": \"2026-09-21\",\n            \"Item\": \"HOST-1\",\n            \"Qty\": 10,\n            \"MovementType\": \"Add\",\n            \"Warehouse\": \"MAIN\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Invoices (posted; PDF)

GET/v1/accounting/uniconta-invoices/:id

Get one posted invoice by {InvoiceNumber}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-invoices?take=100&order_by=InvoiceNumber&descending=1

List posted sales invoices (DebtorInvoiceClient; kind=creditor for purchase invoices). Filters e.g. Account, InvoiceNumber, OrderNumber, Date 01-01-2026..31-03-2026. action=lines + invoice_number lists the item lines (InvTransClient). action=pdf returns the PDF as {mime, content_base64}: by guid (GET /Invoice/GetInvoicePDF/{guid}) or by invoice_number (+ account_number and date, looked up when omitted). Invoices are created by generating them from an order (orders tool, action=generate_invoice).

Minimal query: {"take":100,"order_by":"InvoiceNumber","descending":true}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
order_byInvoiceNumber
descending1
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-invoices?take=100&order_by=InvoiceNumber&descending=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-invoices?take=100&order_by=InvoiceNumber&descending=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-invoices?take=100&order_by=InvoiceNumber&descending=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Items (products)

GET/v1/accounting/uniconta-items/:id

Get one InvItemClient by {Item} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-items/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-items/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-items/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-items?take=100

Query InvItemClient (POST /Query/Get). Inventory items and services (InvItemClient): Item, Name, ItemType, EAN, CostPrice, SalesPrice1-3, Unit, Group, Blocked, Warehouse. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-items?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-items?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-items?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-items

Insert a InvItemClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=InvItemClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Item":"HOST-1","Name":"Hosting","ItemType":1,"SalesPrice1":800,"Group":"1"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Item": "HOST-1",
    "Name": "Hosting",
    "ItemType": 1,
    "SalesPrice1": 800,
    "Group": "1"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-items' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Item": "HOST-1",
    "Name": "Hosting",
    "ItemType": 1,
    "SalesPrice1": 800,
    "Group": "1"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-items', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Item": "HOST-1",
      "Name": "Hosting",
      "ItemType": 1,
      "SalesPrice1": 800,
      "Group": "1"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-items');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Item\": \"HOST-1\",\n    \"Name\": \"Hosting\",\n    \"ItemType\": 1,\n    \"SalesPrice1\": 800,\n    \"Group\": \"1\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-items/:id

Update a InvItemClient by {Item}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Hosting"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Hosting"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-items/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Hosting"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-items/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Hosting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-items/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Hosting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-items/:id

Delete a InvItemClient by {Item} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-items/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-items/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-items/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Orders (sales; kind=creditor for purchase)

GET/v1/accounting/uniconta-orders/:id

Get one order by {OrderNumber} (kind=creditor over HTTP for a purchase order).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-orders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-orders/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-orders?take=100

List sales orders (DebtorOrderClient; kind=creditor for purchase orders, CreditorOrderClient). action=lines + order_number lists an order's lines. Query params: filters, skip, take, order_by, descending; other keys are equality filters (e.g. Account).

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-orders?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-orders?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-orders?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-orders

Create an order: the order fields (Account required; Currency, DeliveryDate, Remark, …) plus lines[] ({Item or Text, Qty, Price, Vat, …}) in one call (POST /Crud/InsertMasterAndDetails), or without lines (POST /Crud/Insert). action=add_lines + order_number + lines[] adds lines to an existing order. action=generate_invoice + order_number BOOKS THE INVOICE from the order (POST /Invoice/GenerateDebtorInvoice; date, simulate validates only, send_email, send_xml) and returns the invoice PDF as {mime, content_base64}; the posted invoice is then readable on the invoices tool (filter OrderNumber). kind=creditor switches to purchase orders and GenerateCreditorInvoice (+ invoice_number, the supplier's).

Minimal body: {"Account":"C1001","lines":[{"Item":"HOST-1","Qty":1,"Price":800,"Text":"Hosting september"}]}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Account": "C1001",
    "lines": [
        {
            "Item": "HOST-1",
            "Qty": 1,
            "Price": 800,
            "Text": "Hosting september"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-orders' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Account": "C1001",
    "lines": [
        {
            "Item": "HOST-1",
            "Qty": 1,
            "Price": 800,
            "Text": "Hosting september"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Account": "C1001",
      "lines": [
          {
              "Item": "HOST-1",
              "Qty": 1,
              "Price": 800,
              "Text": "Hosting september"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-orders');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Account\": \"C1001\",\n    \"lines\": [\n        {\n            \"Item\": \"HOST-1\",\n            \"Qty\": 1,\n            \"Price\": 800,\n            \"Text\": \"Hosting september\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-orders/:id

Update an order by {OrderNumber} (fetched, merged, PUT /Crud/Update).

Minimal body: {"Remark":"Ship Friday"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Remark": "Ship Friday"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-orders/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Remark": "Ship Friday"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-orders/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Remark": "Ship Friday"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Remark\": \"Ship Friday\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-orders/:id

Delete an order by {OrderNumber}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-orders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-orders/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Payment terms

GET/v1/accounting/uniconta-payment-terms/:id

Get one PaymentTermClient by {Payment} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-payment-terms?take=100

Query PaymentTermClient (POST /Query/Get). Payment terms (PaymentTermClient): Payment, Name, Days, PaymentMethod. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-payment-terms?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-payment-terms?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-payment-terms?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-payment-terms

Insert a PaymentTermClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=PaymentTermClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Payment":"8D","Name":"Net 8 days","Days":8}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Payment": "8D",
    "Name": "Net 8 days",
    "Days": 8
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-payment-terms' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Payment": "8D",
    "Name": "Net 8 days",
    "Days": 8
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-payment-terms', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Payment": "8D",
      "Name": "Net 8 days",
      "Days": 8
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-payment-terms');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Payment\": \"8D\",\n    \"Name\": \"Net 8 days\",\n    \"Days\": 8\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-payment-terms/:id

Update a PaymentTermClient by {Payment}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Net 8 days"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Net 8 days"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Net 8 days"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Net 8 days"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Net 8 days\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-payment-terms/:id

Delete a PaymentTermClient by {Payment} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Production orders

GET/v1/accounting/uniconta-production/:id

Get one production order by {OrderNumber}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-production/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-production/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-production/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-production?take=100

List production orders (ProductionOrderClient). action=lines + order_number lists an order's lines (ProductionOrderLineClient).

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-production?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-production?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-production?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-production

Create a production order (fields of ProductionOrderClient: ProdItem, ProdQty, Warehouse, …; lines[] adds lines in the same call). action=create_lines + order_number explodes the BOM into lines (POST /Production/CreateProductionLines; storage_register, overwrite_lines, production_time); action=report_finished + order_number reports it finished (POST /Production/ReportAsFinished; date, text, trans_type, comment, fixed_voucher, simulate, number_serie, partly).

Minimal body: {"ProdItem":"KIT-1","ProdQty":5}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ProdItem": "KIT-1",
    "ProdQty": 5
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-production' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ProdItem": "KIT-1",
    "ProdQty": 5
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-production', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ProdItem": "KIT-1",
      "ProdQty": 5
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-production');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ProdItem\": \"KIT-1\",\n    \"ProdQty\": 5\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-production/:id

Update a production order by {OrderNumber}.

Minimal body: {"ProdQty":6}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ProdQty": 6
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-production/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ProdQty": 6
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-production/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ProdQty": 6
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-production/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ProdQty\": 6\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-production/:id

Delete a production order by {OrderNumber}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-production/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-production/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-production/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Project journals (time, items, costs)

GET/v1/accounting/uniconta-project-journals/:id

Get one project journal by {Journal}.

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-project-journals/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-project-journals/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-project-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-project-journals?take=100

List project journals (ProjectJournalClient). action=lines + journal lists a journal's lines (ProjectJournalLineClient: Date, Project, Task, PrCategory, Employee, Item, Qty, SalesPrice, CostPrice, Text, Invoiceable).

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-project-journals?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-project-journals?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-project-journals?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-project-journals

Without action: insert lines — journal + lines[] (POST /Crud/InsertList/ProjectJournalLineClient). action=check validates (POST /Project/CheckJournal {journal, date, project}); action=post_daily posts the journal (POST /Project/PostDailyJournal {journal, date, comment, project}); action=post_employee posts an employee's time lines (POST /Project/PostEmpJournal {employee, lines[], date, comment, simulate}).

Minimal body: {"journal":"TIME","lines":[{"Date":"2026-09-21","Project":"P100","Employee":"E1","PrCategory":"HOURS","Qty":2.5,"Text":"Design review"}]}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "journal": "TIME",
    "lines": [
        {
            "Date": "2026-09-21",
            "Project": "P100",
            "Employee": "E1",
            "PrCategory": "HOURS",
            "Qty": 2.5,
            "Text": "Design review"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-project-journals' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "journal": "TIME",
    "lines": [
        {
            "Date": "2026-09-21",
            "Project": "P100",
            "Employee": "E1",
            "PrCategory": "HOURS",
            "Qty": 2.5,
            "Text": "Design review"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-project-journals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "journal": "TIME",
      "lines": [
          {
              "Date": "2026-09-21",
              "Project": "P100",
              "Employee": "E1",
              "PrCategory": "HOURS",
              "Qty": 2.5,
              "Text": "Design review"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-project-journals');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"journal\": \"TIME\",\n    \"lines\": [\n        {\n            \"Date\": \"2026-09-21\",\n            \"Project\": \"P100\",\n            \"Employee\": \"E1\",\n            \"PrCategory\": \"HOURS\",\n            \"Qty\": 2.5,\n            \"Text\": \"Design review\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Projects

GET/v1/accounting/uniconta-projects/:id

Get one ProjectClient by {Number} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-projects/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-projects?take=100

Query ProjectClient (POST /Query/Get). Projects (ProjectClient): Number, Name, Group, Status, Debtor, dates, budgets; the journals live on the project-journals tool. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-projects?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-projects?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-projects?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-projects

Insert a ProjectClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=ProjectClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Number":"P100","Name":"Website relaunch","Group":"1"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Number": "P100",
    "Name": "Website relaunch",
    "Group": "1"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-projects' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Number": "P100",
    "Name": "Website relaunch",
    "Group": "1"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-projects', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Number": "P100",
      "Name": "Website relaunch",
      "Group": "1"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-projects');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Number\": \"P100\",\n    \"Name\": \"Website relaunch\",\n    \"Group\": \"1\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-projects/:id

Update a ProjectClient by {Number}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Website relaunch"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Website relaunch"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-projects/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Website relaunch"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-projects/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Website relaunch"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Website relaunch\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-projects/:id

Delete a ProjectClient by {Number} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-projects/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Settlements (open items)

GET/v1/accounting/uniconta-settlements?account_number=C1001

Open items on a debtor (DebtorTransOpenClient; kind=creditor for CreditorTransOpenClient): account_number filters by Account; action=posted lists all posted debtor/creditor transactions (DebtorTransClient / CreditorTransClient) instead. Query params: filters, skip, take, order_by, descending.

Minimal query: {"account_number":"C1001"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
account_numberC1001
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-settlements?account_number=C1001' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-settlements?account_number=C1001', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-settlements?account_number=C1001');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-settlements

action=settle settles open transactions against each other (POST /Transaction/Settle{Debtor|Creditor}Transactions {master: the debtor/creditor record or its Account, settles[]: the open transactions}); action=reopen reopens a settlement (POST /Transaction/ReOpen{Debtor|Creditor}Transaction {master, settle}). kind = debtor (default) | creditor.

Required body: action, master.

Minimal body: {"action":"settle","master":"C1001"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "settle",
    "kind": "debtor",
    "master": "C1001",
    "settles": [
        {
            "RowId": 1234
        },
        {
            "RowId": 1240
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-settlements' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "settle",
    "kind": "debtor",
    "master": "C1001",
    "settles": [
        {
            "RowId": 1234
        },
        {
            "RowId": 1240
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-settlements', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "settle",
      "kind": "debtor",
      "master": "C1001",
      "settles": [
          {
              "RowId": 1234
          },
          {
              "RowId": 1240
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-settlements');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"settle\",\n    \"kind\": \"debtor\",\n    \"master\": \"C1001\",\n    \"settles\": [\n        {\n            \"RowId\": 1234\n        },\n        {\n            \"RowId\": 1240\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Subscriptions

GET/v1/accounting/uniconta-subscriptions/:id

Get one SubscriptionClient by {RowId} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-subscriptions?take=100

Query SubscriptionClient (POST /Query/Get). Recurring invoicing (SubscriptionClient) with its invoices, lines, charges and price lists as sibling tables (use the entities tool for those). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-subscriptions?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-subscriptions?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-subscriptions?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-subscriptions

Insert a SubscriptionClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=SubscriptionClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"DCAccount":"C1001","Name":"Hosting monthly"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "DCAccount": "C1001",
    "Name": "Hosting monthly"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-subscriptions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "DCAccount": "C1001",
    "Name": "Hosting monthly"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-subscriptions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "DCAccount": "C1001",
      "Name": "Hosting monthly"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-subscriptions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"DCAccount\": \"C1001\",\n    \"Name\": \"Hosting monthly\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-subscriptions/:id

Update a SubscriptionClient by {RowId}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Hosting monthly"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Hosting monthly"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Hosting monthly"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Hosting monthly"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Hosting monthly\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-subscriptions/:id

Delete a SubscriptionClient by {RowId} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — VAT codes

GET/v1/accounting/uniconta-vat/:id

Get one GLVatClient by {Vat} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-vat/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-vat/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-vat/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-vat?take=100

Query GLVatClient (POST /Query/Get). VAT codes (GLVatClient): Vat, Name, Rate, VatType (sales/purchase), the accounts posted to. filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-vat?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-vat?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-vat?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-vat

Insert a GLVatClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=GLVatClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Vat":"I25","Name":"K\u00f8bsmoms 25%","Rate":25}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Vat": "I25",
    "Name": "Købsmoms 25%",
    "Rate": 25
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-vat' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Vat": "I25",
    "Name": "Købsmoms 25%",
    "Rate": 25
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-vat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Vat": "I25",
      "Name": "Købsmoms 25%",
      "Rate": 25
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-vat');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Vat\": \"I25\",\n    \"Name\": \"Købsmoms 25%\",\n    \"Rate\": 25\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-vat/:id

Update a GLVatClient by {Vat}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"K\u00f8bsmoms 25%"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Købsmoms 25%"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-vat/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Købsmoms 25%"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-vat/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Købsmoms 25%"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-vat/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Købsmoms 25%\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-vat/:id

Delete a GLVatClient by {Vat} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-vat/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-vat/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-vat/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Uniconta — Warehouses

GET/v1/accounting/uniconta-warehouses/:id

Get one InvWarehouseClient by {Warehouse} (a query on the key property).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/uniconta-warehouses?take=100

Query InvWarehouseClient (POST /Query/Get). Warehouses (InvWarehouseClient) and locations (InvLocationClient via the entities tool). filters = [{property, value}] (Uniconta filter values; ranges and wildcards per its filter grammar), skip, take, order_by, descending; any other key becomes an equality filter. Returns {entity, items}.

Minimal query: {"take":100}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
take100
curl -X GET 'https://api.endpointr.com/v1/accounting/uniconta-warehouses?take=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-warehouses?take=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-warehouses?take=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/uniconta-warehouses

Insert a InvWarehouseClient (POST /Crud/Insert). The record fields are Uniconta's own (call the entities tool with action=fields, entity=InvWarehouseClient for the list); required fields are not published by Uniconta and are enforced server-side.

Minimal body: {"Warehouse":"MAIN","Name":"Main warehouse"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Warehouse": "MAIN",
    "Name": "Main warehouse"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/uniconta-warehouses' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Warehouse": "MAIN",
    "Name": "Main warehouse"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-warehouses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Warehouse": "MAIN",
      "Name": "Main warehouse"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-warehouses');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Warehouse\": \"MAIN\",\n    \"Name\": \"Main warehouse\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/uniconta-warehouses/:id

Update a InvWarehouseClient by {Warehouse}: the current record is fetched, the fields sent are merged, and the full record is PUT to /Crud/Update.

Minimal body: {"Name":"Main warehouse"}

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "Name": "Main warehouse"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "Name": "Main warehouse"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "Name": "Main warehouse"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"Name\": \"Main warehouse\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/uniconta-warehouses/:id

Delete a InvWarehouseClient by {Warehouse} (fetched first; DELETE /Crud/Delete takes the record).

_Requires stored credentials: uniconta (PUT /v1/credentials/uniconta)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/uniconta-warehouses/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Accounting years

GET/v1/accounting/economic-accounting-years/:id

Get one accounting year by {year}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-accounting-years/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounting-years/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounting-years/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-accounting-years?action=entries&id=2026&pagesize=1000

List accounting years, or with action + id (the year, e.g. 2026): entries, totals, vouchers, periods (+ period for one), period_entries / period_totals (period required).

Minimal query: {"action":"entries","id":"2026","pagesize":1000}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
actionentries
id2026
pagesize1000
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-accounting-years?action=entries&id=2026&pagesize=1000' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounting-years?action=entries&id=2026&pagesize=1000', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounting-years?action=entries&id=2026&pagesize=1000');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-accounting-years

Open an accounting year (classic POST).

Minimal body: {"fromDate":"2027-01-01","toDate":"2027-12-31"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "fromDate": "2027-01-01",
    "toDate": "2027-12-31"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-accounting-years' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "fromDate": "2027-01-01",
    "toDate": "2027-12-31"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounting-years', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "fromDate": "2027-01-01",
      "toDate": "2027-12-31"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounting-years');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"fromDate\": \"2027-01-01\",\n    \"toDate\": \"2027-12-31\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-accounting-years/:id

Delete an accounting year by {year} (accounting-years domain API).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-accounting-years/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounting-years/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounting-years/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Accounts (chart)

GET/v1/accounting/economic-accounts/:id

Get one account by {accountNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-accounts?pagesize=1000

The chart of accounts (classic: accountNumber, accountType profitAndLoss|status|heading|totalFrom|sumInterval, balance, vatAccount, debitCredit). With action + id (accountNumber): accounting_years, entries (year required, period optional), totals (same). Domain read-outs: action=key_figure_codes, action=total_intervals [+ id]. Classic list params: filter, sort, skippages, pagesize.

Minimal query: {"pagesize":1000}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize1000
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-accounts?pagesize=1000' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounts?pagesize=1000', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounts?pagesize=1000');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-accounts

Create an account (accounts domain API): number, name, type, isBarred, isBlockedForDirectEntries, isDepartmentMandatory, isUnitMandatory, vatCode, contraAccountNumber, …. action=total_interval creates a total interval instead.

Minimal body: {"number":7325,"name":"Cloud hosting","type":"profitAndLoss","isBarred":false,"isBlockedForDirectEntries":false,"isDepartmentMandatory":false,"isUnitMandatory":false,"vatCode":"I25"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "number": 7325,
    "name": "Cloud hosting",
    "type": "profitAndLoss",
    "isBarred": false,
    "isBlockedForDirectEntries": false,
    "isDepartmentMandatory": false,
    "isUnitMandatory": false,
    "vatCode": "I25"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "number": 7325,
    "name": "Cloud hosting",
    "type": "profitAndLoss",
    "isBarred": false,
    "isBlockedForDirectEntries": false,
    "isDepartmentMandatory": false,
    "isUnitMandatory": false,
    "vatCode": "I25"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "number": 7325,
      "name": "Cloud hosting",
      "type": "profitAndLoss",
      "isBarred": false,
      "isBlockedForDirectEntries": false,
      "isDepartmentMandatory": false,
      "isUnitMandatory": false,
      "vatCode": "I25"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"number\": 7325,\n    \"name\": \"Cloud hosting\",\n    \"type\": \"profitAndLoss\",\n    \"isBarred\": false,\n    \"isBlockedForDirectEntries\": false,\n    \"isDepartmentMandatory\": false,\n    \"isUnitMandatory\": false,\n    \"vatCode\": \"I25\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-accounts/:id

Update an account (domain PUT: the full record with objectVersion; {id} fills number).

Minimal body: {"name":"Cloud hosting","objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Cloud hosting",
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Cloud hosting",
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Cloud hosting",
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Cloud hosting\",\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-accounts/:id

Delete an account by {accountNumber} (domain API).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-accounts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Agreements (companies)

GET/v1/accounting/economic-agreements

List the e-conomic agreements (companies) configured for this customer: every economic:<label> row with a grant token, each confirmed against /self with our app secret (fills in agreement_number and account_name on rows saved without them). Returns {agreements:[{label, name, agreement_number, agreement_type, ok, error?}], app_configured, account_required, has_legacy_row} — labels only, never tokens. Pass a label as the account selector on every other e-conomic tool. This tool takes no selector.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-agreements' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-agreements', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-agreements');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — App roles

GET/v1/accounting/economic-app-roles/:id

Get one by {roleNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-app-roles/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-app-roles/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-app-roles/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-app-roles?pagesize=100

List app-roles. The API roles (Superuser, Bookkeeping, Sales, Project Employee) and the modules each needs. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-app-roles?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-app-roles?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-app-roles?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Booked entries (ledger)

GET/v1/accounting/economic-booked-entries?filter=date%24gte%3A2026-09-01%24and%3Adate%24lte%3A2026-09-30&paged=1&pageSize=100&sort=date

The ledger (booked entries domain API): every posted line, filter on accountNumber/date/voucherNumber/customerNumber/supplierNumber (e.g. date$gte:2026-09-01$and:date$lte:2026-09-30), cursor list or paged=true (pageSize ≤ 100, sort=date), count=true. action=matched_pairs lists matched pairs; action=changelog the dimension change log.

Minimal query: {"filter":"date$gte:2026-09-01$and:date$lte:2026-09-30","paged":true,"pageSize":100,"sort":"date"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
filterdate$gte:2026-09-01$and:date$lte:2026-09-30
paged1
pageSize100
sortdate
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-booked-entries?filter=date%24gte%3A2026-09-01%24and%3Adate%24lte%3A2026-09-30&paged=1&pageSize=100&sort=date' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-booked-entries?filter=date%24gte%3A2026-09-01%24and%3Adate%24lte%3A2026-09-30&paged=1&pageSize=100&sort=date', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-booked-entries?filter=date%24gte%3A2026-09-01%24and%3Adate%24lte%3A2026-09-30&paged=1&pageSize=100&sort=date');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-booked-entries

action=match {entry_ids: [...]} matches booked entries (open-item settlement).

Required body: action, entry_ids.

Minimal body: {"action":"match","entry_ids":[501,502]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "match",
    "entry_ids": [
        501,
        502
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-booked-entries' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "match",
    "entry_ids": [
        501,
        502
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-booked-entries', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "match",
      "entry_ids": [
          501,
          502
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-booked-entries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"match\",\n    \"entry_ids\": [\n        501,\n        502\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Budget figures

GET/v1/accounting/economic-budgets/:id

Get one budget figure by {number}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-budgets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-budgets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-budgets?filter=accountNumber%24eq%3A7320

List budget figures (budgets domain API; cursor, paged=true, count=true, filter).

Minimal query: {"filter":"accountNumber$eq:7320"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
filteraccountNumber$eq:7320
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-budgets?filter=accountNumber%24eq%3A7320' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-budgets?filter=accountNumber%24eq%3A7320', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-budgets?filter=accountNumber%24eq%3A7320');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-budgets

Create a budget figure: accountNumber, amountDefaultCurrency, fromDate, toDate.

Minimal body: {"accountNumber":7320,"amountDefaultCurrency":12000,"fromDate":"2026-01-01","toDate":"2026-12-31"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "accountNumber": 7320,
    "amountDefaultCurrency": 12000,
    "fromDate": "2026-01-01",
    "toDate": "2026-12-31"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-budgets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "accountNumber": 7320,
    "amountDefaultCurrency": 12000,
    "fromDate": "2026-01-01",
    "toDate": "2026-12-31"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-budgets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "accountNumber": 7320,
      "amountDefaultCurrency": 12000,
      "fromDate": "2026-01-01",
      "toDate": "2026-12-31"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-budgets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"accountNumber\": 7320,\n    \"amountDefaultCurrency\": 12000,\n    \"fromDate\": \"2026-01-01\",\n    \"toDate\": \"2026-12-31\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-budgets/:id

Update a budget figure (domain PUT with objectVersion; {id} fills number).

Minimal body: {"amountDefaultCurrency":15000,"objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "amountDefaultCurrency": 15000,
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-budgets/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "amountDefaultCurrency": 15000,
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-budgets/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "amountDefaultCurrency": 15000,
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"amountDefaultCurrency\": 15000,\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-budgets/:id

Delete a budget figure by {number}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-budgets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-budgets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-budgets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Currencies

GET/v1/accounting/economic-currencies/:id

Get one by {code}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-currencies/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-currencies/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-currencies/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-currencies?pagesize=100

List currencies. ISO 4217 currencies. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-currencies?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-currencies?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-currencies?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Customer groups

GET/v1/accounting/economic-customer-groups/:id

Get one by {customerGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-customer-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-customer-groups?pagesize=100

List customer-groups. Customer groups (each carries the receivables account). Sub-links via action (with id = the customerGroupNumber): customers. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-customer-groups?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customer-groups?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customer-groups?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-customer-groups

Create in customer-groups. Body forwarded to e-conomic verbatim.

Minimal body: {"customerGroupNumber":2,"name":"Web customers","account":{"accountNumber":5600}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "customerGroupNumber": 2,
    "name": "Web customers",
    "account": {
        "accountNumber": 5600
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-customer-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "customerGroupNumber": 2,
    "name": "Web customers",
    "account": {
        "accountNumber": 5600
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customer-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "customerGroupNumber": 2,
      "name": "Web customers",
      "account": {
          "accountNumber": 5600
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customer-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"customerGroupNumber\": 2,\n    \"name\": \"Web customers\",\n    \"account\": {\n        \"accountNumber\": 5600\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-customer-groups/:id

Replace one by {customerGroupNumber} (classic PUT sends the full record).

Minimal body: {"customerGroupNumber":2,"name":"Web customers","account":{"accountNumber":5600}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "customerGroupNumber": 2,
    "name": "Web customers",
    "account": {
        "accountNumber": 5600
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-customer-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "customerGroupNumber": 2,
    "name": "Web customers",
    "account": {
        "accountNumber": 5600
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "customerGroupNumber": 2,
      "name": "Web customers",
      "account": {
          "accountNumber": 5600
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"customerGroupNumber\": 2,\n    \"name\": \"Web customers\",\n    \"account\": {\n        \"accountNumber\": 5600\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-customer-groups/:id

Delete one by {customerGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-customer-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customer-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Customers

GET/v1/accounting/economic-customers/:id

Get one customer by {customerNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-customers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-customers?filter=name%24like%3AExample&pagesize=100

List customers (filter, sort, skippages, pagesize). With action + id (customerNumber): contacts [+ contact_id], delivery_locations [+ location_id], invoices, invoices_drafts, invoices_booked, totals, templates. action=setup reads the agreement's sales defaults (domain API).

Minimal query: {"filter":"name$like:Example","pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
filtername$like:Example
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-customers?filter=name%24like%3AExample&pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customers?filter=name%24like%3AExample&pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customers?filter=name%24like%3AExample&pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-customers

Create a customer (name, customerGroup{customerGroupNumber}, vatZone{vatZoneNumber}, currency, paymentTerms{paymentTermsNumber} required by e-conomic). action=contact / delivery_location with id (customerNumber) creates the sub-record.

Minimal body: {"name":"Example ApS","customerGroup":{"customerGroupNumber":1},"vatZone":{"vatZoneNumber":1},"currency":"DKK","paymentTerms":{"paymentTermsNumber":1},"email":"billing@example.com"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS",
    "customerGroup": {
        "customerGroupNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "email": "billing@example.com"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-customers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS",
    "customerGroup": {
        "customerGroupNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "email": "billing@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS",
      "customerGroup": {
          "customerGroupNumber": 1
      },
      "vatZone": {
          "vatZoneNumber": 1
      },
      "currency": "DKK",
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "email": "billing@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\",\n    \"customerGroup\": {\n        \"customerGroupNumber\": 1\n    },\n    \"vatZone\": {\n        \"vatZoneNumber\": 1\n    },\n    \"currency\": \"DKK\",\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"email\": \"billing@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-customers/:id

Replace a customer by {customerNumber} (PUT, full record). action=patch sends a PATCH with only the fields given. action=contact / delivery_location with sub_id updates a sub-record.

Minimal body: {"name":"Example ApS","customerGroup":{"customerGroupNumber":1},"vatZone":{"vatZoneNumber":1},"currency":"DKK","paymentTerms":{"paymentTermsNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS",
    "customerGroup": {
        "customerGroupNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-customers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS",
    "customerGroup": {
        "customerGroupNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS",
      "customerGroup": {
          "customerGroupNumber": 1
      },
      "vatZone": {
          "vatZoneNumber": 1
      },
      "currency": "DKK",
      "paymentTerms": {
          "paymentTermsNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\",\n    \"customerGroup\": {\n        \"customerGroupNumber\": 1\n    },\n    \"vatZone\": {\n        \"vatZoneNumber\": 1\n    },\n    \"currency\": \"DKK\",\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-customers/:id

Delete a customer by {customerNumber}; ?action=contact&sub_id= or delivery_location deletes a sub-record.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-customers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-customers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Departmental distributions

GET/v1/accounting/economic-departmental-distributions/:id

Get one by {departmentalDistributionNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-departmental-distributions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-departmental-distributions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-departmental-distributions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-departmental-distributions?pagesize=100

List departmental-distributions. Distribution keys across departments. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-departmental-distributions?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-departmental-distributions?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-departmental-distributions?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Departments

GET/v1/accounting/economic-departments/:id

Get one by {departmentNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-departments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-departments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-departments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-departments?pagesize=100

List departments. Departments (dimension). Writes live on the dimensions domain API. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-departments?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-departments?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-departments?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Dimensions

GET/v1/accounting/economic-dimensions?kind=values&filter=dimensionNumber%24eq%3A1

Dimensions domain API. kind = dimensions (default; id for one) | values (dimension + key for one) | distributions (read-only) | accounts | booked_entries | budget_figures | draft_entries | sales_document_lines | booked_invoice_lines (dimension data). Cursor list, paged=true, count=true, filter.

Minimal query: {"kind":"values","filter":"dimensionNumber$eq:1"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kindvalues
filterdimensionNumber$eq:1
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-dimensions?kind=values&filter=dimensionNumber%24eq%3A1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-dimensions?kind=values&filter=dimensionNumber%24eq%3A1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-dimensions?kind=values&filter=dimensionNumber%24eq%3A1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-dimensions

Create a dimension (name, active), a value (dimensionNumber, key, name, active) or dimension data (kind, keys per family; bulk=true + items posts an array).

Minimal body: {"kind":"values","dimensionNumber":1,"key":"MKT","name":"Marketing","active":true}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "values",
    "dimensionNumber": 1,
    "key": "MKT",
    "name": "Marketing",
    "active": true
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-dimensions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "values",
    "dimensionNumber": 1,
    "key": "MKT",
    "name": "Marketing",
    "active": true
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-dimensions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "values",
      "dimensionNumber": 1,
      "key": "MKT",
      "name": "Marketing",
      "active": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-dimensions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"values\",\n    \"dimensionNumber\": 1,\n    \"key\": \"MKT\",\n    \"name\": \"Marketing\",\n    \"active\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-dimensions/:id

Update (domain PUT with objectVersion; for dimensions {id} fills number; bulk=true + items for dimension data).

Minimal body: {"kind":"dimensions","name":"Department","active":true,"objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "dimensions",
    "name": "Department",
    "active": true,
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-dimensions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "dimensions",
    "name": "Department",
    "active": true,
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-dimensions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "dimensions",
      "name": "Department",
      "active": true,
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-dimensions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"dimensions\",\n    \"name\": \"Department\",\n    \"active\": true,\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-dimensions/:id

Delete a dimension by {number}; for values and dimension data the record's keys ride as query params (?kind=values&dimensionNumber=1&key=MKT).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-dimensions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-dimensions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-dimensions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Documents (voucher attachments)

GET/v1/accounting/economic-documents/:id

Get one attached document by {number}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-documents/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-documents/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-documents/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-documents?filter=voucherNumber%24eq%3A12

List attached documents (documents domain API; cursor, paged=true, count=true, filter). action=pdf (id) returns the document as base64.

Minimal query: {"filter":"voucherNumber$eq:12"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
filtervoucherNumber$eq:12
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-documents?filter=voucherNumber%24eq%3A12' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-documents?filter=voucherNumber%24eq%3A12', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-documents?filter=voucherNumber%24eq%3A12');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-documents

Attach a PDF/JPEG/PNG to a voucher: voucher_number, accounting_year, file_name, content_base64, optional mime, note, on_conflict (Merge|Fail). Multipart upload done by the relay.

Required body: voucher_number, accounting_year, file_name, content_base64.

Minimal body: {"voucher_number":12,"accounting_year":"2026","file_name":"receipt.pdf","content_base64":"<base64>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "voucher_number": 12,
    "accounting_year": "2026",
    "file_name": "receipt.pdf",
    "content_base64": "<base64>"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-documents' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "voucher_number": 12,
    "accounting_year": "2026",
    "file_name": "receipt.pdf",
    "content_base64": "<base64>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-documents', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "voucher_number": 12,
      "accounting_year": "2026",
      "file_name": "receipt.pdf",
      "content_base64": "<base64>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-documents');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"voucher_number\": 12,\n    \"accounting_year\": \"2026\",\n    \"file_name\": \"receipt.pdf\",\n    \"content_base64\": \"<base64>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-documents/:id

Delete an attached document by {number}; ?action=bulk&numbers=1,2,3 deletes several.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-documents/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-documents/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-documents/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Draft entries and booking

GET/v1/accounting/economic-draft-entries/:id

Get one draft entry by {entryNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-draft-entries/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-draft-entries?filter=journalNumber%24eq%3A1

List draft entries (journals domain API; cursor list, or paged=true with pageSize ≤ 100, skipPages, sort; count=true for the number; filter as journalNumber$eq:1). action=accruals lists accruals (journal + entry for one).

Minimal query: {"filter":"journalNumber$eq:1"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
filterjournalNumber$eq:1
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-draft-entries?filter=journalNumber%24eq%3A1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-draft-entries?filter=journalNumber%24eq%3A1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-draft-entries?filter=journalNumber%24eq%3A1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-draft-entries

Create a draft entry: journalNumber (or journal), entryTypeNumber (1 = finance voucher), date, amount (debit-positive), currency, accountNumber, contraAccountNumber?, vatCode?, text, voucherNumber? (auto when omitted). entries = an array (bulk, ≤ 500). BOOKING: action=book (journal) books every draft in the journal; action=book_entries (journal, entry_numbers[]); action=book_interval (journal, from, to). action=accrual creates an accrual.

Minimal body: {"journalNumber":1,"entryTypeNumber":1,"date":"2026-09-30","amount":100,"currency":"DKK","accountNumber":7320,"contraAccountNumber":5820,"vatCode":"I25","text":"Correction"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "journalNumber": 1,
    "entryTypeNumber": 1,
    "date": "2026-09-30",
    "amount": 100,
    "currency": "DKK",
    "accountNumber": 7320,
    "contraAccountNumber": 5820,
    "vatCode": "I25",
    "text": "Correction"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-draft-entries' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "journalNumber": 1,
    "entryTypeNumber": 1,
    "date": "2026-09-30",
    "amount": 100,
    "currency": "DKK",
    "accountNumber": 7320,
    "contraAccountNumber": 5820,
    "vatCode": "I25",
    "text": "Correction"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-draft-entries', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "journalNumber": 1,
      "entryTypeNumber": 1,
      "date": "2026-09-30",
      "amount": 100,
      "currency": "DKK",
      "accountNumber": 7320,
      "contraAccountNumber": 5820,
      "vatCode": "I25",
      "text": "Correction"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-draft-entries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"journalNumber\": 1,\n    \"entryTypeNumber\": 1,\n    \"date\": \"2026-09-30\",\n    \"amount\": 100,\n    \"currency\": \"DKK\",\n    \"accountNumber\": 7320,\n    \"contraAccountNumber\": 5820,\n    \"vatCode\": \"I25\",\n    \"text\": \"Correction\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-draft-entries/:id

Update a draft entry (domain PUT with objectVersion; {id} fills entryNumber; entries array = bulk). action=accrual updates an accrual.

Minimal body: {"amount":120,"objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "amount": 120,
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-draft-entries/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "amount": 120,
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "amount": 120,
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"amount\": 120,\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-draft-entries/:id

Delete a draft entry by {entryNumber}; ?action=delete_journal deletes every draft in journal {id}; ?action=accrual deletes an accrual.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-draft-entries/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-draft-entries/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Employees

GET/v1/accounting/economic-employees/:id

Get one by {employeeNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-employees/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-employees/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-employees/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-employees?pagesize=100

List employees. Employees (sales persons) with their customers and invoices. Sub-links via action (with id = the employeeNumber): customers, draft_invoices, booked_invoices. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-employees?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-employees?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-employees?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Invoices

GET/v1/accounting/economic-invoices/:id

Get one invoice by {id} = kind:number (e.g. booked:1126); a bare number is a draft.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-invoices?kind=booked&filter=date%24gte%3A2026-09-01&pagesize=100

List invoices by kind = drafts (default) | booked | sent | paid | unpaid | overdue | not_due | totals (classic filter, sort, skippages, pagesize forwarded). action=pdf (kind + id) returns the PDF as base64; action=attachment (drafts, id) the draft's attachment; action=lines / booked_lines [+ id] read lines through the q2c domain API.

Minimal query: {"kind":"booked","filter":"date$gte:2026-09-01","pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kindbooked
filterdate$gte:2026-09-01
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-invoices?kind=booked&filter=date%24gte%3A2026-09-01&pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-invoices?kind=booked&filter=date%24gte%3A2026-09-01&pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-invoices?kind=booked&filter=date%24gte%3A2026-09-01&pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-invoices

Create a DRAFT invoice (date, currency, customer{customerNumber}, recipient{name, vatZone}, layout{layoutNumber}, paymentTerms{paymentTermsNumber}, lines[{product{productNumber}, description, quantity, unitNetPrice}]). action=book (id = draft number) BOOKS it (POST /invoices/booked). action=mark_sent (id = booked number) registers it as sent. action=line (id = draft number) adds a line via the q2c API (lines array = bulk).

Minimal body: {"date":"2026-09-01","currency":"DKK","customer":{"customerNumber":12},"recipient":{"name":"Example ApS","vatZone":{"vatZoneNumber":1}},"layout":{"layoutNumber":19},"paymentTerms":{"paymentTermsNumber":1},"lines":[{"product":{"productNumber":"HOST-1"},"description":"Hosting september","quantity":1,"unitNetPrice":800}]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": [
        {
            "product": {
                "productNumber": "HOST-1"
            },
            "description": "Hosting september",
            "quantity": 1,
            "unitNetPrice": 800
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-invoices' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": [
        {
            "product": {
                "productNumber": "HOST-1"
            },
            "description": "Hosting september",
            "quantity": 1,
            "unitNetPrice": 800
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01",
      "currency": "DKK",
      "customer": {
          "customerNumber": 12
      },
      "recipient": {
          "name": "Example ApS",
          "vatZone": {
              "vatZoneNumber": 1
          }
      },
      "layout": {
          "layoutNumber": 19
      },
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "lines": [
          {
              "product": {
                  "productNumber": "HOST-1"
              },
              "description": "Hosting september",
              "quantity": 1,
              "unitNetPrice": 800
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-invoices');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\",\n    \"currency\": \"DKK\",\n    \"customer\": {\n        \"customerNumber\": 12\n    },\n    \"recipient\": {\n        \"name\": \"Example ApS\",\n        \"vatZone\": {\n            \"vatZoneNumber\": 1\n        }\n    },\n    \"layout\": {\n        \"layoutNumber\": 19\n    },\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"lines\": [\n        {\n            \"product\": {\n                \"productNumber\": \"HOST-1\"\n            },\n            \"description\": \"Hosting september\",\n            \"quantity\": 1,\n            \"unitNetPrice\": 800\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-invoices/:id

Replace a draft invoice by {draftInvoiceNumber} (PUT). action=line updates a draft line (q2c domain API, objectVersion).

Minimal body: {"date":"2026-09-01","currency":"DKK","customer":{"customerNumber":12},"recipient":{"name":"Example ApS","vatZone":{"vatZoneNumber":1}},"layout":{"layoutNumber":19},"paymentTerms":{"paymentTermsNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-invoices/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-invoices/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01",
      "currency": "DKK",
      "customer": {
          "customerNumber": 12
      },
      "recipient": {
          "name": "Example ApS",
          "vatZone": {
              "vatZoneNumber": 1
          }
      },
      "layout": {
          "layoutNumber": 19
      },
      "paymentTerms": {
          "paymentTermsNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\",\n    \"currency\": \"DKK\",\n    \"customer\": {\n        \"customerNumber\": 12\n    },\n    \"recipient\": {\n        \"name\": \"Example ApS\",\n        \"vatZone\": {\n            \"vatZoneNumber\": 1\n        }\n    },\n    \"layout\": {\n        \"layoutNumber\": 19\n    },\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-invoices/:id

Delete a draft invoice by {draftInvoiceNumber}; ?action=line&line= deletes one draft line.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-invoices/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Journals (kassekladder)

GET/v1/accounting/economic-journals/:id

Get one journal by {journalNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-journals/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-journals/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-journals?action=vouchers&id=1

List journals (classic). With action + id (journalNumber): vouchers [+ voucher], entries, templates [+ template financeVoucher|manualCustomerInvoice]. Booking lives on the draft-entries tool.

Minimal query: {"action":"vouchers","id":1}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
actionvouchers
id1
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-journals?action=vouchers&id=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-journals?action=vouchers&id=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-journals?action=vouchers&id=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-journals

Create a journal (journals domain API: name, allowPartialBooking, autoApprove, balancingBehavior, isStandingJournal, lockVatCodes, repeatText, requireAdminApproval, requireBalancePerVoucherId). action=voucher with id (journalNumber) creates vouchers on the classic journal — the body is one voucher or vouchers = an array (entries.financeVouchers[] etc. per journal templates).

Minimal body: {"name":"Manual entries","allowPartialBooking":false,"autoApprove":false,"balancingBehavior":"voucher","isStandingJournal":false,"lockVatCodes":false,"repeatText":false,"requireAdminApproval":false,"requireBalancePerVoucherId":true}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Manual entries",
    "allowPartialBooking": false,
    "autoApprove": false,
    "balancingBehavior": "voucher",
    "isStandingJournal": false,
    "lockVatCodes": false,
    "repeatText": false,
    "requireAdminApproval": false,
    "requireBalancePerVoucherId": true
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-journals' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Manual entries",
    "allowPartialBooking": false,
    "autoApprove": false,
    "balancingBehavior": "voucher",
    "isStandingJournal": false,
    "lockVatCodes": false,
    "repeatText": false,
    "requireAdminApproval": false,
    "requireBalancePerVoucherId": true
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-journals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Manual entries",
      "allowPartialBooking": false,
      "autoApprove": false,
      "balancingBehavior": "voucher",
      "isStandingJournal": false,
      "lockVatCodes": false,
      "repeatText": false,
      "requireAdminApproval": false,
      "requireBalancePerVoucherId": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-journals');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Manual entries\",\n    \"allowPartialBooking\": false,\n    \"autoApprove\": false,\n    \"balancingBehavior\": \"voucher\",\n    \"isStandingJournal\": false,\n    \"lockVatCodes\": false,\n    \"repeatText\": false,\n    \"requireAdminApproval\": false,\n    \"requireBalancePerVoucherId\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-journals/:id

Update a journal (domain PUT, objectVersion; {id} fills number). action=voucher + voucher replaces a classic voucher.

Minimal body: {"name":"Manual entries","objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Manual entries",
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-journals/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Manual entries",
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-journals/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Manual entries",
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Manual entries\",\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-journals/:id

Delete a journal by {journalNumber} (domain); ?action=voucher&voucher= deletes a classic voucher.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-journals/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-journals/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-journals/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Layouts

GET/v1/accounting/economic-layouts/:id

Get one by {layoutNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-layouts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-layouts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-layouts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-layouts?pagesize=100

List layouts. Document layouts; layoutNumber is required on a draft invoice. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-layouts?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-layouts?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-layouts?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Orders

GET/v1/accounting/economic-orders/:id

Get one order by {id} = kind:number; a bare number is a draft.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-orders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-orders/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-orders?kind=drafts&pagesize=100

List orders by kind = drafts (default) | sent | archived. action=pdf (kind + id); action=lines [+ id] via the q2c API.

Minimal query: {"kind":"drafts","pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kinddrafts
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-orders?kind=drafts&pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-orders?kind=drafts&pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-orders?kind=drafts&pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-orders

Create a draft order (same shape as a draft invoice).

Minimal body: {"date":"2026-09-01","currency":"DKK","customer":{"customerNumber":12},"recipient":{"name":"Example ApS","vatZone":{"vatZoneNumber":1}},"layout":{"layoutNumber":19},"paymentTerms":{"paymentTermsNumber":1},"lines":[]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": []
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-orders' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01",
      "currency": "DKK",
      "customer": {
          "customerNumber": 12
      },
      "recipient": {
          "name": "Example ApS",
          "vatZone": {
              "vatZoneNumber": 1
          }
      },
      "layout": {
          "layoutNumber": 19
      },
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "lines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-orders');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\",\n    \"currency\": \"DKK\",\n    \"customer\": {\n        \"customerNumber\": 12\n    },\n    \"recipient\": {\n        \"name\": \"Example ApS\",\n        \"vatZone\": {\n            \"vatZoneNumber\": 1\n        }\n    },\n    \"layout\": {\n        \"layoutNumber\": 19\n    },\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"lines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-orders/:id

Replace a draft order by {orderNumber} (PUT).

Minimal body: {"date":"2026-09-01"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-orders/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-orders/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-orders/:id

Delete a draft order by {orderNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-orders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-orders/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-orders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Payment terms

GET/v1/accounting/economic-payment-terms/:id

Get one by {paymentTermsNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-payment-terms/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-payment-terms?pagesize=100

List payment-terms. Payment terms; paymentTermsNumber is required on customers and draft invoices. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-payment-terms?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-terms?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-terms?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-payment-terms

Create in payment-terms. Body forwarded to e-conomic verbatim.

Minimal body: {"name":"Net 8 days","paymentTermsType":"net","daysOfCredit":8}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Net 8 days",
    "paymentTermsType": "net",
    "daysOfCredit": 8
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-payment-terms' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Net 8 days",
    "paymentTermsType": "net",
    "daysOfCredit": 8
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-terms', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Net 8 days",
      "paymentTermsType": "net",
      "daysOfCredit": 8
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-terms');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Net 8 days\",\n    \"paymentTermsType\": \"net\",\n    \"daysOfCredit\": 8\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-payment-terms/:id

Replace one by {paymentTermsNumber} (classic PUT sends the full record).

Minimal body: {"name":"Net 8 days","paymentTermsType":"net","daysOfCredit":8}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Net 8 days",
    "paymentTermsType": "net",
    "daysOfCredit": 8
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-payment-terms/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Net 8 days",
    "paymentTermsType": "net",
    "daysOfCredit": 8
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Net 8 days",
      "paymentTermsType": "net",
      "daysOfCredit": 8
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Net 8 days\",\n    \"paymentTermsType\": \"net\",\n    \"daysOfCredit\": 8\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-payment-terms/:id

Delete one by {paymentTermsNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-payment-terms/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-terms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Payment types

GET/v1/accounting/economic-payment-types/:id

Get one by {paymentTypeNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-payment-types/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-types/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-types/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-payment-types?pagesize=100

List payment-types. Payment types. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-payment-types?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-payment-types?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-payment-types?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Product groups

GET/v1/accounting/economic-product-groups/:id

Get one by {productGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-product-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-product-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-product-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-product-groups?pagesize=100

List product-groups. Product groups (sales accounts per VAT zone; inventoryEnabled). Sub-links via action (with id = the productGroupNumber): products, sales_accounts. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-product-groups?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-product-groups?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-product-groups?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-product-groups

Create in product-groups. Body forwarded to e-conomic verbatim.

Minimal body: {"productGroupNumber":2,"name":"Services","salesAccounts":[]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productGroupNumber": 2,
    "name": "Services",
    "salesAccounts": []
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-product-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productGroupNumber": 2,
    "name": "Services",
    "salesAccounts": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-product-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productGroupNumber": 2,
      "name": "Services",
      "salesAccounts": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-product-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productGroupNumber\": 2,\n    \"name\": \"Services\",\n    \"salesAccounts\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-product-groups/:id

Replace one by {productGroupNumber} (classic PUT sends the full record).

Minimal body: {"productGroupNumber":2,"name":"Services","salesAccounts":[]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productGroupNumber": 2,
    "name": "Services",
    "salesAccounts": []
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-product-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productGroupNumber": 2,
    "name": "Services",
    "salesAccounts": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-product-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productGroupNumber": 2,
      "name": "Services",
      "salesAccounts": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-product-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productGroupNumber\": 2,\n    \"name\": \"Services\",\n    \"salesAccounts\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-product-groups/:id

Delete one by {productGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-product-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-product-groups/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-product-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Products

GET/v1/accounting/economic-products/:id

Get one product by {productNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-products/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-products?pagesize=100

List products (classic). action + id (productNumber): invoices, pricing. Domain families via kind: price_groups [+ id], special_prices (price_group), sales_prices (prices in currencies; id + currency for one).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-products?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-products?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-products?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-products

Create a product (productNumber, name, salesPrice, productGroup{productGroupNumber}). kind=price_groups / special_prices (price_group) / sales_prices (id = productNumber) creates through the domain API.

Minimal body: {"productNumber":"HOST-1","name":"Hosting","salesPrice":800,"productGroup":{"productGroupNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productNumber": "HOST-1",
    "name": "Hosting",
    "salesPrice": 800,
    "productGroup": {
        "productGroupNumber": 1
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-products' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productNumber": "HOST-1",
    "name": "Hosting",
    "salesPrice": 800,
    "productGroup": {
        "productGroupNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productNumber": "HOST-1",
      "name": "Hosting",
      "salesPrice": 800,
      "productGroup": {
          "productGroupNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-products');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productNumber\": \"HOST-1\",\n    \"name\": \"Hosting\",\n    \"salesPrice\": 800,\n    \"productGroup\": {\n        \"productGroupNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-products/:id

Replace a product by {productNumber} (PUT). With kind the domain families are updated (objectVersion).

Minimal body: {"productNumber":"HOST-1","name":"Hosting","salesPrice":900,"productGroup":{"productGroupNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "productNumber": "HOST-1",
    "name": "Hosting",
    "salesPrice": 900,
    "productGroup": {
        "productGroupNumber": 1
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-products/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "productNumber": "HOST-1",
    "name": "Hosting",
    "salesPrice": 900,
    "productGroup": {
        "productGroupNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-products/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "productNumber": "HOST-1",
      "name": "Hosting",
      "salesPrice": 900,
      "productGroup": {
          "productGroupNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"productNumber\": \"HOST-1\",\n    \"name\": \"Hosting\",\n    \"salesPrice\": 900,\n    \"productGroup\": {\n        \"productGroupNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-products/:id

Delete a product by {productNumber}; ?kind=price_groups / sales_prices (+ currency) deletes a domain record.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-products/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Projects (time, mileage, activities)

GET/v1/accounting/economic-projects/:id

Get one by {number} (?kind= picks the resource; default projects).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-projects/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-projects?kind=time_entries&filter=date%24gte%3A2026-09-01

Projects domain API behind one tool, picked with kind: projects (default), groups, statuses, customers, activities, activity_groups, cost_types, cost_type_groups, employees, employee_groups, project_employees, project_employee_groups, project_activities, time_entries, time_entry_prices, mileages, mileage_prices. id reads one; action=allowed (projects/activities with employeeNumber / projectNumber). Cursor, paged=true, count=true, filter.

Minimal query: {"kind":"time_entries","filter":"date$gte:2026-09-01"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kindtime_entries
filterdate$gte:2026-09-01
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-projects?kind=time_entries&filter=date%24gte%3A2026-09-01' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-projects?kind=time_entries&filter=date%24gte%3A2026-09-01', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-projects?kind=time_entries&filter=date%24gte%3A2026-09-01');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-projects

Create a record of kind (e.g. time_entries: activityNumber, date, employeeNumber, projectNumber, hours). action=approve with numbers[] [+ bookOn] approves time entries or mileages.

Minimal body: {"kind":"time_entries","projectNumber":1,"activityNumber":1,"employeeNumber":1,"date":"2026-09-21","hours":2}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "time_entries",
    "projectNumber": 1,
    "activityNumber": 1,
    "employeeNumber": 1,
    "date": "2026-09-21",
    "hours": 2
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-projects' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "time_entries",
    "projectNumber": 1,
    "activityNumber": 1,
    "employeeNumber": 1,
    "date": "2026-09-21",
    "hours": 2
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-projects', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "time_entries",
      "projectNumber": 1,
      "activityNumber": 1,
      "employeeNumber": 1,
      "date": "2026-09-21",
      "hours": 2
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-projects');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"time_entries\",\n    \"projectNumber\": 1,\n    \"activityNumber\": 1,\n    \"employeeNumber\": 1,\n    \"date\": \"2026-09-21\",\n    \"hours\": 2\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-projects/:id

Update a record of kind (domain PUT with objectVersion; {id} fills number).

Minimal body: {"kind":"projects","name":"Renamed","projectGroupNumber":1,"objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "projects",
    "name": "Renamed",
    "projectGroupNumber": 1,
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-projects/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "projects",
    "name": "Renamed",
    "projectGroupNumber": 1,
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-projects/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "projects",
      "name": "Renamed",
      "projectGroupNumber": 1,
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"projects\",\n    \"name\": \"Renamed\",\n    \"projectGroupNumber\": 1,\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-projects/:id

Delete a record of kind by {number} (?kind=).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-projects/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Quotes

GET/v1/accounting/economic-quotes/:id

Get one quote by {id} = kind:number; a bare number is a draft.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-quotes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-quotes/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-quotes?kind=drafts&pagesize=100

List quotes by kind = drafts (default) | sent | archived. action=pdf (kind + id); action=lines [+ id] via the q2c API.

Minimal query: {"kind":"drafts","pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kinddrafts
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-quotes?kind=drafts&pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-quotes?kind=drafts&pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-quotes?kind=drafts&pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-quotes

Create a draft quote (same shape as a draft invoice).

Minimal body: {"date":"2026-09-01","currency":"DKK","customer":{"customerNumber":12},"recipient":{"name":"Example ApS","vatZone":{"vatZoneNumber":1}},"layout":{"layoutNumber":19},"paymentTerms":{"paymentTermsNumber":1},"lines":[]}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": []
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-quotes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01",
    "currency": "DKK",
    "customer": {
        "customerNumber": 12
    },
    "recipient": {
        "name": "Example ApS",
        "vatZone": {
            "vatZoneNumber": 1
        }
    },
    "layout": {
        "layoutNumber": 19
    },
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "lines": []
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-quotes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01",
      "currency": "DKK",
      "customer": {
          "customerNumber": 12
      },
      "recipient": {
          "name": "Example ApS",
          "vatZone": {
              "vatZoneNumber": 1
          }
      },
      "layout": {
          "layoutNumber": 19
      },
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "lines": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-quotes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\",\n    \"currency\": \"DKK\",\n    \"customer\": {\n        \"customerNumber\": 12\n    },\n    \"recipient\": {\n        \"name\": \"Example ApS\",\n        \"vatZone\": {\n            \"vatZoneNumber\": 1\n        }\n    },\n    \"layout\": {\n        \"layoutNumber\": 19\n    },\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"lines\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-quotes/:id

Replace a draft quote by {quoteNumber} (PUT).

Minimal body: {"date":"2026-09-01"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-01"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-quotes/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-01"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-quotes/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-01"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-01\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-quotes/:id

Delete a draft quote by {quoteNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-quotes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-quotes/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Self (agreement, settings, modules)

GET/v1/accounting/economic-self

The agreement behind the selected grant (/self: agreementNumber, agreementType, user, company, bankInformation, settings, modules). action=app_settings reads /app-settings; action=modules reads /modules.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-self' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-self', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-self');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Subscriptions

GET/v1/accounting/economic-subscriptions/:id

Get one by {number} (?kind=).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-subscriptions?kind=subscriptions

Subscriptions domain API: kind = subscriptions (default) | subscribers | lines (subscription = number for one subscription's lines). id reads one; cursor, paged=true, count=true, filter.

Minimal query: {"kind":"subscriptions"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
kindsubscriptions
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-subscriptions?kind=subscriptions' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-subscriptions?kind=subscriptions', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-subscriptions?kind=subscriptions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-subscriptions

Create a subscription (name, interval, collection), a subscriber (subscriptionNumber, customerNumber, startDate, endDate) or a line (kind=lines, subscription).

Minimal body: {"kind":"subscriptions","name":"Hosting monthly","interval":"month","collection":"proportional"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "subscriptions",
    "name": "Hosting monthly",
    "interval": "month",
    "collection": "proportional"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-subscriptions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "subscriptions",
    "name": "Hosting monthly",
    "interval": "month",
    "collection": "proportional"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-subscriptions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "subscriptions",
      "name": "Hosting monthly",
      "interval": "month",
      "collection": "proportional"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-subscriptions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"subscriptions\",\n    \"name\": \"Hosting monthly\",\n    \"interval\": \"month\",\n    \"collection\": \"proportional\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-subscriptions/:id

Update (domain PUT with objectVersion; {id} fills number).

Minimal body: {"kind":"subscriptions","name":"Renamed","objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "kind": "subscriptions",
    "name": "Renamed",
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-subscriptions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "kind": "subscriptions",
    "name": "Renamed",
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "kind": "subscriptions",
      "name": "Renamed",
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"kind\": \"subscriptions\",\n    \"name\": \"Renamed\",\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-subscriptions/:id

Delete by {number} (?kind=; lines need subscription).

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Supplier groups

GET/v1/accounting/economic-supplier-groups/:id

Get one by {supplierGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-supplier-groups?pagesize=100

List supplier-groups. Supplier groups (each carries the payables account). Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-supplier-groups?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-supplier-groups?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-supplier-groups?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-supplier-groups

Create in supplier-groups. Body forwarded to e-conomic verbatim.

Minimal body: {"supplierGroupNumber":2,"name":"Hosting","account":{"accountNumber":6400}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "supplierGroupNumber": 2,
    "name": "Hosting",
    "account": {
        "accountNumber": 6400
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-supplier-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "supplierGroupNumber": 2,
    "name": "Hosting",
    "account": {
        "accountNumber": 6400
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-supplier-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "supplierGroupNumber": 2,
      "name": "Hosting",
      "account": {
          "accountNumber": 6400
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-supplier-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"supplierGroupNumber\": 2,\n    \"name\": \"Hosting\",\n    \"account\": {\n        \"accountNumber\": 6400\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-supplier-groups/:id

Replace one by {supplierGroupNumber} (classic PUT sends the full record).

Minimal body: {"supplierGroupNumber":2,"name":"Hosting","account":{"accountNumber":6400}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "supplierGroupNumber": 2,
    "name": "Hosting",
    "account": {
        "accountNumber": 6400
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "supplierGroupNumber": 2,
    "name": "Hosting",
    "account": {
        "accountNumber": 6400
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "supplierGroupNumber": 2,
      "name": "Hosting",
      "account": {
          "accountNumber": 6400
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"supplierGroupNumber\": 2,\n    \"name\": \"Hosting\",\n    \"account\": {\n        \"accountNumber\": 6400\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-supplier-groups/:id

Delete one by {supplierGroupNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-supplier-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Suppliers

GET/v1/accounting/economic-suppliers/:id

Get one supplier by {supplierNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-suppliers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-suppliers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-suppliers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-suppliers?pagesize=100

List suppliers (classic; filter, sort, skippages, pagesize). action=contacts with id (supplierNumber) [+ contact_id] reads contacts. kind=contacts / kind=groups list through the suppliers domain API.

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-suppliers?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-suppliers?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-suppliers?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-suppliers

Create a supplier (name, supplierGroup{supplierGroupNumber}, currency, paymentTerms{paymentTermsNumber}, vatZone). action=contact / group creates through the suppliers domain API.

Minimal body: {"name":"Hosting Co","supplierGroup":{"supplierGroupNumber":1},"currency":"DKK","paymentTerms":{"paymentTermsNumber":1},"vatZone":{"vatZoneNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting Co",
    "supplierGroup": {
        "supplierGroupNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    }
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-suppliers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting Co",
    "supplierGroup": {
        "supplierGroupNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-suppliers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting Co",
      "supplierGroup": {
          "supplierGroupNumber": 1
      },
      "currency": "DKK",
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "vatZone": {
          "vatZoneNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-suppliers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting Co\",\n    \"supplierGroup\": {\n        \"supplierGroupNumber\": 1\n    },\n    \"currency\": \"DKK\",\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"vatZone\": {\n        \"vatZoneNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-suppliers/:id

Replace a supplier by {supplierNumber} (PUT). action=contact / group updates through the domain API (objectVersion).

Minimal body: {"name":"Hosting Co","supplierGroup":{"supplierGroupNumber":1},"currency":"DKK","paymentTerms":{"paymentTermsNumber":1},"vatZone":{"vatZoneNumber":1}}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting Co",
    "supplierGroup": {
        "supplierGroupNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    }
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-suppliers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting Co",
    "supplierGroup": {
        "supplierGroupNumber": 1
    },
    "currency": "DKK",
    "paymentTerms": {
        "paymentTermsNumber": 1
    },
    "vatZone": {
        "vatZoneNumber": 1
    }
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-suppliers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting Co",
      "supplierGroup": {
          "supplierGroupNumber": 1
      },
      "currency": "DKK",
      "paymentTerms": {
          "paymentTermsNumber": 1
      },
      "vatZone": {
          "vatZoneNumber": 1
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-suppliers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting Co\",\n    \"supplierGroup\": {\n        \"supplierGroupNumber\": 1\n    },\n    \"currency\": \"DKK\",\n    \"paymentTerms\": {\n        \"paymentTermsNumber\": 1\n    },\n    \"vatZone\": {\n        \"vatZoneNumber\": 1\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-suppliers/:id

Delete a supplier by {supplierNumber}; ?action=contact / group deletes a domain record by {number}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-suppliers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-suppliers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-suppliers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Units

GET/v1/accounting/economic-units/:id

Get one by {unitNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-units/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-units/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-units/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-units?pagesize=100

List units. Units of measure. Sub-links via action (with id = the unitNumber): products. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-units?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-units?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-units?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-units

Create in units. Body forwarded to e-conomic verbatim.

Minimal body: {"unitNumber":5,"name":"hours"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "unitNumber": 5,
    "name": "hours"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-units' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "unitNumber": 5,
    "name": "hours"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-units', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "unitNumber": 5,
      "name": "hours"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-units');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"unitNumber\": 5,\n    \"name\": \"hours\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-units/:id

Replace one by {unitNumber} (classic PUT sends the full record).

Minimal body: {"unitNumber":5,"name":"hours"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "unitNumber": 5,
    "name": "hours"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-units/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "unitNumber": 5,
    "name": "hours"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-units/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "unitNumber": 5,
      "name": "hours"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-units/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"unitNumber\": 5,\n    \"name\": \"hours\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-units/:id

Delete one by {unitNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-units/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-units/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-units/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — VAT accounts

GET/v1/accounting/economic-vat-accounts/:id

Get one by {vatCode}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-vat-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-vat-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-vat-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-vat-accounts?pagesize=100

List vat-accounts. VAT accounts: the vatCode (I25, U25, …), its rate, type and the ledger account behind it. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-vat-accounts?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-vat-accounts?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-vat-accounts?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — VAT zones

GET/v1/accounting/economic-vat-zones/:id

Get one by {vatZoneNumber}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-vat-zones/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-vat-zones/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-vat-zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-vat-zones?pagesize=100

List vat-zones. VAT zones (domestic, EU, abroad…); vatZoneNumber is required on customers and draft invoices. Classic list params: skippages, pagesize (max 1000), filter (prop$eq:value, $and:/$or:/$in:/$like:), sort (-prop for descending).

Minimal query: {"pagesize":100}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
pagesize100
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-vat-zones?pagesize=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-vat-zones?pagesize=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-vat-zones?pagesize=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

e-conomic — Webhooks

GET/v1/accounting/economic-webhooks/:id

Get one webhook by {eventType}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-webhooks/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/accounting/economic-webhooks

List the agreement's webhooks (SuperUser role; one per event type, HTTPS only). action=event_types lists the event names (e.g. INVOICE_BOOKED, CUSTOMER_UPDATED) and their merge fields.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/accounting/economic-webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/accounting/economic-webhooks

Create a webhook: eventType, name, url (https), contentType, optional postData, customHeaders[{key,value}] (max 5), isDisabled.

Minimal body: {"eventType":"INVOICE_BOOKED","name":"Invoice booked \u2192 endpointr","url":"https://example.com/hooks/economic","contentType":"application/json"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "eventType": "INVOICE_BOOKED",
    "name": "Invoice booked → endpointr",
    "url": "https://example.com/hooks/economic",
    "contentType": "application/json"
}
curl -X POST 'https://api.endpointr.com/v1/accounting/economic-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "eventType": "INVOICE_BOOKED",
    "name": "Invoice booked → endpointr",
    "url": "https://example.com/hooks/economic",
    "contentType": "application/json"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "eventType": "INVOICE_BOOKED",
      "name": "Invoice booked → endpointr",
      "url": "https://example.com/hooks/economic",
      "contentType": "application/json"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"eventType\": \"INVOICE_BOOKED\",\n    \"name\": \"Invoice booked → endpointr\",\n    \"url\": \"https://example.com/hooks/economic\",\n    \"contentType\": \"application/json\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/accounting/economic-webhooks/:id

Update a webhook (domain PUT with objectVersion; {id} = eventType).

Minimal body: {"name":"Renamed","url":"https://example.com/hooks/economic","contentType":"application/json","objectVersion":"<objectVersion>"}

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Renamed",
    "url": "https://example.com/hooks/economic",
    "contentType": "application/json",
    "objectVersion": "<objectVersion>"
}
curl -X PUT 'https://api.endpointr.com/v1/accounting/economic-webhooks/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Renamed",
    "url": "https://example.com/hooks/economic",
    "contentType": "application/json",
    "objectVersion": "<objectVersion>"
}'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-webhooks/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Renamed",
      "url": "https://example.com/hooks/economic",
      "contentType": "application/json",
      "objectVersion": "<objectVersion>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Renamed\",\n    \"url\": \"https://example.com/hooks/economic\",\n    \"contentType\": \"application/json\",\n    \"objectVersion\": \"<objectVersion>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/accounting/economic-webhooks/:id

Delete a webhook by {eventType}.

_Requires stored credentials: economic (PUT /v1/credentials/economic)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/accounting/economic-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/accounting/economic-webhooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/accounting/economic-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Bindings

Bindings

GET/v1/bindings/bindings

List all your bindings.

A binding wires a trigger event (e.g. stripe.customer.created) to an action on another provider (e.g. paypal.customer.create) via a path-based mapping.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/bindings/bindings' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/bindings/bindings', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/bindings');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/bindings/bindings/:id

Retrieve one binding by id.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/bindings/bindings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/bindings/bindings/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/bindings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/bindings/bindings

Create a binding. mapping values of the form $.foo.bar pull from the event data; literal strings/numbers are passed through.

Example below: when a customer is created in Stripe, mirror them into PayPal, keeping the Stripe id as an external reference.

Required body: trigger_event, action.

Minimal body: {"trigger_event":"stripe.customer.created","action":"paypal.customer.create"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "trigger_event": "stripe.customer.created",
    "action": "paypal.customer.create",
    "enabled": true,
    "mapping": {
        "email": "$.object.email",
        "name": "$.object.name",
        "external_reference": "$.object.id"
    }
}
curl -X POST 'https://api.endpointr.com/v1/bindings/bindings' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "trigger_event": "stripe.customer.created",
    "action": "paypal.customer.create",
    "enabled": true,
    "mapping": {
        "email": "$.object.email",
        "name": "$.object.name",
        "external_reference": "$.object.id"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/bindings/bindings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "trigger_event": "stripe.customer.created",
      "action": "paypal.customer.create",
      "enabled": true,
      "mapping": {
          "email": "$.object.email",
          "name": "$.object.name",
          "external_reference": "$.object.id"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/bindings');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"trigger_event\": \"stripe.customer.created\",\n    \"action\": \"paypal.customer.create\",\n    \"enabled\": true,\n    \"mapping\": {\n        \"email\": \"\$.object.email\",\n        \"name\": \"\$.object.name\",\n        \"external_reference\": \"\$.object.id\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/bindings/bindings/:id

Update a binding (PUT replaces all fields).

Minimal body: {"trigger_event":"stripe.customer.created","action":"paypal.customer.create","enabled":false,"mapping":[]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "trigger_event": "stripe.customer.created",
    "action": "paypal.customer.create",
    "enabled": false,
    "mapping": []
}
curl -X PUT 'https://api.endpointr.com/v1/bindings/bindings/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "trigger_event": "stripe.customer.created",
    "action": "paypal.customer.create",
    "enabled": false,
    "mapping": []
}'
const response = await fetch('https://api.endpointr.com/v1/bindings/bindings/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "trigger_event": "stripe.customer.created",
      "action": "paypal.customer.create",
      "enabled": false,
      "mapping": []
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/bindings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"trigger_event\": \"stripe.customer.created\",\n    \"action\": \"paypal.customer.create\",\n    \"enabled\": false,\n    \"mapping\": []\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/bindings/bindings/:id

Delete a binding.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/bindings/bindings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/bindings/bindings/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/bindings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Inbound Webhooks

GET/v1/bindings/inbound-webhooks

List your provider webhook endpoints. Each row is an opaque token that addresses a (customer, provider, secret) tuple.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/bindings/inbound-webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/bindings/inbound-webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/inbound-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/bindings/inbound-webhooks

Register an inbound webhook endpoint. The response returns a token — paste {baseUrl}/v1/inbound/{provider}/{token} into the provider's webhook settings dashboard.

The secret you supply is the signing secret from the provider (e.g. Stripe's whsec_…, QuickPay's private key, or PayPal's webhook_id). Incoming webhooks are signature-verified against it before being dispatched to bindings.

Required body: provider, secret.

Minimal body: {"provider":"stripe","secret":"whsec_..."}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "provider": "stripe",
    "secret": "whsec_...",
    "label": "Production Stripe"
}
curl -X POST 'https://api.endpointr.com/v1/bindings/inbound-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "provider": "stripe",
    "secret": "whsec_...",
    "label": "Production Stripe"
}'
const response = await fetch('https://api.endpointr.com/v1/bindings/inbound-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "provider": "stripe",
      "secret": "whsec_...",
      "label": "Production Stripe"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/inbound-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"provider\": \"stripe\",\n    \"secret\": \"whsec_...\",\n    \"label\": \"Production Stripe\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/bindings/inbound-webhooks/:id

Delete an endpoint by its token. Use the token string as the path id.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/bindings/inbound-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/bindings/inbound-webhooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/bindings/inbound-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cal.com

Cal.com — Accounts (labels)

GET/v1/calcom/cal-com-accounts

List the Cal.com account labels configured for this customer (stored as calcom:<label>). Returns {accounts:[…], has_default:bool} — never secrets. Pass one of these labels as the account selector on the other Cal.com tools. This tool takes no account selector.

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cal.com — Bookings

GET/v1/calcom/cal-com-bookings/:id

Fetch one booking by its uid (the uid string from a create/list response, e.g. bmDs8j…).

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-bookings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-bookings/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-bookings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/calcom/cal-com-bookings?status=upcoming&limit=10

List bookings, newest first. All filters optional; call with {} to list all.

Filters: status (upcoming|recurring|past|cancelled|unconfirmed), attendeeEmail, attendeeName, eventTypeId, eventTypeIds (comma-separated), bookingUid, afterStart, beforeEnd (date strings), afterCreatedAt/beforeCreatedAt, cursor (pagination), limit (default 50).

Auth: vault calcom — or calcom:<label> + an account selector for multiple accounts.

Minimal query: {"status":"upcoming","limit":"10"}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
statusupcoming
limit10
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-bookings?status=upcoming&limit=10' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-bookings?status=upcoming&limit=10', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-bookings?status=upcoming&limit=10');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/calcom/cal-com-bookings

Create a booking, or act on an existing booking via an action field. Without action, this creates a booking.

Create a booking (no action). Required: start (ISO 8601 UTC), eventTypeId, and attendee {name, email, timeZone}. Get eventTypeId from /v1/calcom/cal-com-event-types and a valid start from /v1/calcom/cal-com-slots.

Minimal:

{"start":"2026-08-13T09:00:00Z","eventTypeId":123,"attendee":{"name":"John Doe","email":"john@example.com","timeZone":"America/New_York"}}

Alt event identifier: eventTypeSlug + username instead of eventTypeId. Optional: lengthInMinutes (variable-length event types), guests (extra attendee emails), location/meetingUrl, bookingFieldsResponses, metadata.

Act on an existing booking — pass action + bookingUid:

Cancel (reason optional):

{"action":"cancel","bookingUid":"bmDs8j…","cancellationReason":"User requested"}

Reschedule (needs a new start; reschedulingReason optional):
{"action":"reschedule","bookingUid":"bmDs8j…","start":"2026-08-13T10:00:00Z"}

Confirm / decline an unconfirmed booking:
{"action":"confirm","bookingUid":"bmDs8j…"}

Multi-account: pass the top-level account selector on any of these.

Minimal body: {"start":"2026-08-13T09:00:00Z","eventTypeId":123,"attendee":{"name":"John Doe","email":"john@example.com","timeZone":"America/New_York"}}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "start": "2026-08-13T09:00:00Z",
    "eventTypeId": 123,
    "attendee": {
        "name": "John Doe",
        "email": "john@example.com",
        "timeZone": "America/New_York"
    }
}
curl -X POST 'https://api.endpointr.com/v1/calcom/cal-com-bookings' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "start": "2026-08-13T09:00:00Z",
    "eventTypeId": 123,
    "attendee": {
        "name": "John Doe",
        "email": "john@example.com",
        "timeZone": "America/New_York"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-bookings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "start": "2026-08-13T09:00:00Z",
      "eventTypeId": 123,
      "attendee": {
          "name": "John Doe",
          "email": "john@example.com",
          "timeZone": "America/New_York"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-bookings');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"start\": \"2026-08-13T09:00:00Z\",\n    \"eventTypeId\": 123,\n    \"attendee\": {\n        \"name\": \"John Doe\",\n        \"email\": \"john@example.com\",\n        \"timeZone\": \"America/New_York\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cal.com — Event Types

GET/v1/calcom/cal-com-event-types/:id

Fetch one event type by its numeric id (full config: length, locations, booking fields, schedule).

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-event-types/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-event-types/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-event-types/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/calcom/cal-com-event-types?username=jane

List Cal.com event types — the bookable meeting types. This is the discovery step: each event type's numeric id is the eventTypeId that /v1/calcom/cal-com-slots and /v1/calcom/cal-com-bookings require.

All filters are optional; call with no params to list everything visible to the stored API key. Filters: username, usernames (comma-separated), eventSlug (needs username), orgSlug, orgId, sortCreatedAt (asc|desc).

Auth: vault calcom (PUT /v1/credentials/calcom {api_key}), or calcom:<label> + an account selector for multiple accounts.

Minimal query: {"username":"jane"}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
usernamejane
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-event-types?username=jane' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-event-types?username=jane', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-event-types?username=jane');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cal.com — Schedules (availability)

GET/v1/calcom/cal-com-schedules

List the availability schedules (working-hours definitions) on the account. Event types reference a schedule to decide when they're bookable. Multi-account: pass account to choose which stored credential to read.

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-schedules' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-schedules', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-schedules');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/calcom/cal-com-schedules/:id

Fetch one schedule by its numeric id (name, timeZone, availability windows, overrides).

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-schedules/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/calcom/cal-com-schedules

Create an availability schedule. Required: name, timeZone (IANA). isDefault defaults to false if omitted; availability is optional and defaults to Mon–Fri 09:00–17:00.

Minimal:

{"name":"Working hours","timeZone":"Europe/Copenhagen"}

With explicit windows and made the default:

{"name":"Working hours","timeZone":"Europe/Copenhagen","isDefault":true,"availability":[{"days":["Monday","Tuesday","Wednesday","Thursday","Friday"],"startTime":"09:00","endTime":"17:00"}]}

Required body: name, timeZone.

Minimal body: {"name":"Working hours","timeZone":"Europe/Copenhagen"}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Working hours",
    "timeZone": "Europe/Copenhagen",
    "isDefault": true,
    "availability": [
        {
            "days": [
                "Monday",
                "Tuesday",
                "Wednesday",
                "Thursday",
                "Friday"
            ],
            "startTime": "09:00",
            "endTime": "17:00"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/calcom/cal-com-schedules' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Working hours",
    "timeZone": "Europe/Copenhagen",
    "isDefault": true,
    "availability": [
        {
            "days": [
                "Monday",
                "Tuesday",
                "Wednesday",
                "Thursday",
                "Friday"
            ],
            "startTime": "09:00",
            "endTime": "17:00"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-schedules', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Working hours",
      "timeZone": "Europe/Copenhagen",
      "isDefault": true,
      "availability": [
          {
              "days": [
                  "Monday",
                  "Tuesday",
                  "Wednesday",
                  "Thursday",
                  "Friday"
              ],
              "startTime": "09:00",
              "endTime": "17:00"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-schedules');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Working hours\",\n    \"timeZone\": \"Europe/Copenhagen\",\n    \"isDefault\": true,\n    \"availability\": [\n        {\n            \"days\": [\n                \"Monday\",\n                \"Tuesday\",\n                \"Wednesday\",\n                \"Thursday\",\n                \"Friday\"\n            ],\n            \"startTime\": \"09:00\",\n            \"endTime\": \"17:00\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/calcom/cal-com-schedules/:id

Update a schedule by id (PATCH upstream — only the fields you send change). Send any of name, timeZone, isDefault, availability, overrides.

Minimal body: {"name":"Working hours (updated)","availability":[{"days":["Monday","Tuesday","Wednesday","Thursday"],"startTime":"10:00","endTime":"18:00"}]}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Working hours (updated)",
    "availability": [
        {
            "days": [
                "Monday",
                "Tuesday",
                "Wednesday",
                "Thursday"
            ],
            "startTime": "10:00",
            "endTime": "18:00"
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/calcom/cal-com-schedules/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Working hours (updated)",
    "availability": [
        {
            "days": [
                "Monday",
                "Tuesday",
                "Wednesday",
                "Thursday"
            ],
            "startTime": "10:00",
            "endTime": "18:00"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Working hours (updated)",
      "availability": [
          {
              "days": [
                  "Monday",
                  "Tuesday",
                  "Wednesday",
                  "Thursday"
              ],
              "startTime": "10:00",
              "endTime": "18:00"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Working hours (updated)\",\n    \"availability\": [\n        {\n            \"days\": [\n                \"Monday\",\n                \"Tuesday\",\n                \"Wednesday\",\n                \"Thursday\"\n            ],\n            \"startTime\": \"10:00\",\n            \"endTime\": \"18:00\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/calcom/cal-com-schedules/:id

Delete a schedule by id.

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/calcom/cal-com-schedules/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-schedules/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cal.com — Slots (availability)

GET/v1/calcom/cal-com-slots?eventTypeId=123&start=2026-08-13&end=2026-08-20&timeZone=America%2FNew_York

Get available time slots for an event type over a date range. The response data is grouped by date (e.g. {"2026-08-13":[{"start":"…"}, …]}).

Required: start + end (ISO 8601 or YYYY-MM-DD, UTC) and an event identifier — either eventTypeId, or eventTypeSlug together with username.

Optional: timeZone (IANA, e.g. America/New_York — slot times come back in this zone), duration (minutes, for variable-length event types).

Minimal:

?eventTypeId=123&start=2026-08-13&end=2026-08-20&timeZone=America/New_York

By slug instead of id:

?eventTypeSlug=intro-call&username=jane&start=2026-08-13&end=2026-08-20

Auth: vault calcom — or calcom:<label> + an account selector for multiple accounts.

Required query: start, end.

Minimal query: {"eventTypeId":"123","start":"2026-08-13","end":"2026-08-20","timeZone":"America/New_York"}

_Requires stored credentials: calcom (PUT /v1/credentials/calcom)._

AuthorizationBearer YOUR_JWT_TOKEN
eventTypeId123
start2026-08-13
end2026-08-20
timeZoneAmerica/New_York
curl -X GET 'https://api.endpointr.com/v1/calcom/cal-com-slots?eventTypeId=123&start=2026-08-13&end=2026-08-20&timeZone=America%2FNew_York' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/calcom/cal-com-slots?eventTypeId=123&start=2026-08-13&end=2026-08-20&timeZone=America%2FNew_York', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/calcom/cal-com-slots?eventTypeId=123&start=2026-08-13&end=2026-08-20&timeZone=America%2FNew_York');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare

Cloudflare — Account labels

GET/v1/cloudflare/cf-account-labels

List the Cloudflare credential labels configured for this customer (stored as cloudflare:<label>, one per company). Returns {accounts:[…], has_default:bool} — never secrets. When more than one is configured, every other Cloudflare tool REQUIRES an account argument naming which login to use — call this first to see the valid labels. Not the same as cf-accounts, which lists the accounts inside one Cloudflare login. This tool takes no account selector.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/cf-account-labels' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cf-account-labels', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cf-account-labels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Accounts

GET/v1/cloudflare/cf-accounts

List the Cloudflare accounts the selected token can access. Use this to discover the account_id for Workers/Pages. Not the credential labels — those come from cf-account-labels.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/cf-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cf-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cf-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/cf-accounts/:id

Get an account by {account_id}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/cf-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cf-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cf-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Cache purge

POST/v1/cloudflare/cache-purge

Purge (clear) a zone's cache. zone_id required in the body (from the Cloudflare Zones tool). Pick one mode: purge_everything:true clears the whole cache, or a non-empty files / tags / hosts / prefixes array does a selective purge (tags/hosts/prefixes are Enterprise-only). Over MCP the zone travels in the body.

Required body: zone_id.

Minimal body: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "purge_everything": true
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/cache-purge' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "purge_everything": true
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cache-purge', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "purge_everything": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cache-purge');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"purge_everything\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Cache settings

GET/v1/cloudflare/cache-settings?zone_id=%3Czone-id%3E

Read a zone's cache-related settings (cache_level, browser_cache_ttl, development_mode, always_online, sort_query_string_for_cache). zone_id required (from the Cloudflare Zones tool).

Required query: zone_id.

Minimal query: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
zone_id<zone-id>
curl -X GET 'https://api.endpointr.com/v1/cloudflare/cache-settings?zone_id=%3Czone-id%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cache-settings?zone_id=%3Czone-id%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cache-settings?zone_id=%3Czone-id%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/cloudflare/cache-settings/:id

Update one cache setting. The path {id} is the setting name; zone_id + value required in the body. development_mode/always_online/sort_query_string_for_cache take on/off; cache_level takes aggressive/basic/simplified; browser_cache_ttl takes an integer of seconds.

Required body: zone_id, value.

Minimal body: {"zone_id":"<zone-id>","value":"on"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "value": "on"
}
curl -X PUT 'https://api.endpointr.com/v1/cloudflare/cache-settings/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "value": "on"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/cache-settings/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "value": "on"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/cache-settings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"value\": \"on\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — DNS records

GET/v1/cloudflare/dns-records/:id

Get one DNS record. Id is the composite zoneId:recordId (over MCP the zone can't travel in the query).

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/dns-records/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/dns-records/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/dns-records/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/dns-records?zone_id=%3Czone-id%3E&type=A

List DNS records for a zone. zone_id is required — get it from the Cloudflare Zones tool (…_zones_query / …_zones_get). Optional filters: type, name, content, page, per_page.

Required query: zone_id.

Minimal query: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
zone_id<zone-id>
typeA
curl -X GET 'https://api.endpointr.com/v1/cloudflare/dns-records?zone_id=%3Czone-id%3E&type=A' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/dns-records?zone_id=%3Czone-id%3E&type=A', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/dns-records?zone_id=%3Czone-id%3E&type=A');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/dns-records

Create a DNS record. zone_id required in the body alongside the record fields.

Required body: zone_id.

Minimal body: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "type": "A",
    "name": "www.example.com",
    "content": "203.0.113.10",
    "ttl": 3600,
    "proxied": false
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/dns-records' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "type": "A",
    "name": "www.example.com",
    "content": "203.0.113.10",
    "ttl": 3600,
    "proxied": false
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/dns-records', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "type": "A",
      "name": "www.example.com",
      "content": "203.0.113.10",
      "ttl": 3600,
      "proxied": false
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/dns-records');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"type\": \"A\",\n    \"name\": \"www.example.com\",\n    \"content\": \"203.0.113.10\",\n    \"ttl\": 3600,\n    \"proxied\": false\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/cloudflare/dns-records/:id

Update a DNS record by {record_id} (PUT — send the full record). zone_id required in the body.

Required body: zone_id.

Minimal body: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "type": "A",
    "name": "www.example.com",
    "content": "203.0.113.20"
}
curl -X PUT 'https://api.endpointr.com/v1/cloudflare/dns-records/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "type": "A",
    "name": "www.example.com",
    "content": "203.0.113.20"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/dns-records/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "type": "A",
      "name": "www.example.com",
      "content": "203.0.113.20"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/dns-records/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"type\": \"A\",\n    \"name\": \"www.example.com\",\n    \"content\": \"203.0.113.20\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/dns-records/:id

Delete a DNS record. Id is the composite zoneId:recordId.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/dns-records/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/dns-records/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/dns-records/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Pages deployments

GET/v1/cloudflare/pages-deployments/:id

Get one deployment. Id is the composite projectName:deploymentId.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/pages-deployments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-deployments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-deployments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/pages-deployments?project_name=my-site

List deployments for a Pages project. project_name is required (from the Cloudflare Pages projects tool — …_pages-projects_list).

Required query: project_name.

Minimal query: {"project_name":"my-site"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
project_namemy-site
curl -X GET 'https://api.endpointr.com/v1/cloudflare/pages-deployments?project_name=my-site' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-deployments?project_name=my-site', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-deployments?project_name=my-site');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/pages-deployments

Trigger a new deployment for a project. project_name required; optional branch.

Required body: project_name.

Minimal body: {"project_name":"my-site"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "project_name": "my-site"
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/pages-deployments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "project_name": "my-site"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-deployments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "project_name": "my-site"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-deployments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"project_name\": \"my-site\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/pages-deployments/:id

Delete a deployment. Id is the composite projectName:deploymentId.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/pages-deployments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-deployments/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-deployments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Pages projects

GET/v1/cloudflare/pages-projects

List Pages projects on the account.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/pages-projects' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-projects', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-projects');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/pages-projects/:id

Get a Pages project by {project_name}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/pages-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-projects/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/pages-projects

Create a Pages project. name required. Optional production_branch, build_config, account_id override.

Required body: name.

Minimal body: {"name":"my-site"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "my-site",
    "production_branch": "main"
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/pages-projects' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "my-site",
    "production_branch": "main"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-projects', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "my-site",
      "production_branch": "main"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-projects');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"my-site\",\n    \"production_branch\": \"main\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/pages-projects/:id

Delete a Pages project by {project_name}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/pages-projects/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/pages-projects/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/pages-projects/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Token verify

GET/v1/cloudflare/token-verify

Verify the stored API token is valid/active. Returns {id, status, …}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/token-verify' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/token-verify', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/token-verify');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Worker routes

GET/v1/cloudflare/worker-routes?zone_id=%3Czone-id%3E

List Worker routes for a zone. zone_id is required (from the Cloudflare Zones tool).

Required query: zone_id.

Minimal query: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
zone_id<zone-id>
curl -X GET 'https://api.endpointr.com/v1/cloudflare/worker-routes?zone_id=%3Czone-id%3E' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/worker-routes?zone_id=%3Czone-id%3E', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/worker-routes?zone_id=%3Czone-id%3E');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/worker-routes

Create a route binding a URL pattern to a script. zone_id required in the body.

Required body: zone_id, pattern.

Minimal body: {"zone_id":"<zone-id>","pattern":"example.com/*"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "pattern": "example.com/*",
    "script": "my-worker"
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/worker-routes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "pattern": "example.com/*",
    "script": "my-worker"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/worker-routes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "pattern": "example.com/*",
      "script": "my-worker"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/worker-routes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"pattern\": \"example.com/*\",\n    \"script\": \"my-worker\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/cloudflare/worker-routes/:id

Update a route by {route_id}. zone_id required in the body.

Required body: zone_id.

Minimal body: {"zone_id":"<zone-id>"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "zone_id": "<zone-id>",
    "pattern": "example.com/api/*",
    "script": "my-worker"
}
curl -X PUT 'https://api.endpointr.com/v1/cloudflare/worker-routes/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "zone_id": "<zone-id>",
    "pattern": "example.com/api/*",
    "script": "my-worker"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/worker-routes/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "zone_id": "<zone-id>",
      "pattern": "example.com/api/*",
      "script": "my-worker"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/worker-routes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"zone_id\": \"<zone-id>\",\n    \"pattern\": \"example.com/api/*\",\n    \"script\": \"my-worker\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/worker-routes/:id

Delete a route. Id is the composite zoneId:routeId.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/worker-routes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/worker-routes/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/worker-routes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Workers

GET/v1/cloudflare/workers

List Worker scripts on the account (account_id defaults to the stored credential; override it with an id from the Cloudflare Accounts tool).

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/workers' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/workers', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/workers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/workers/:id

Get a Worker script's settings/bindings by {script_name}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/workers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/workers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/workers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/workers

Upload (create or replace) a Worker script as an ES module. name + script (the JS) are required. Optional compatibility_date, compatibility_flags, bindings (KV/vars/secrets/etc.), and account_id override.

Required body: name, script.

Minimal body: {"name":"my-worker","script":"export default { async fetch(req, env, ctx) { return new Response('hello'); } }"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "my-worker",
    "script": "export default { async fetch(req, env, ctx) { return new Response('hello'); } }",
    "compatibility_date": "2024-01-01"
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/workers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "my-worker",
    "script": "export default { async fetch(req, env, ctx) { return new Response('\''hello'\''); } }",
    "compatibility_date": "2024-01-01"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/workers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "my-worker",
      "script": "export default { async fetch(req, env, ctx) { return new Response('hello'); } }",
      "compatibility_date": "2024-01-01"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/workers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"my-worker\",\n    \"script\": \"export default { async fetch(req, env, ctx) { return new Response('hello'); } }\",\n    \"compatibility_date\": \"2024-01-01\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/workers/:id

Delete a Worker script by {script_name}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/workers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/workers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/workers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cloudflare — Zones (domains)

GET/v1/cloudflare/zones

List all zones (domains) on the account.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/zones' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/zones/:id

Get one zone by {zone_id}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cloudflare/zones/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/cloudflare/zones?name=acme.com

List zones filtered by name, status, account_id, with page/per_page paging.

Minimal query: {"name":"acme.com"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
nameacme.com
curl -X GET 'https://api.endpointr.com/v1/cloudflare/zones?name=acme.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones?name=acme.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones?name=acme.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/cloudflare/zones

Add a domain. Creates a zone. name is the domain; account_id falls back to the stored credential. Optional type (full|partial).

Required body: name.

Minimal body: {"name":"example.com"}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "example.com"
}
curl -X POST 'https://api.endpointr.com/v1/cloudflare/zones' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/cloudflare/zones/:id

Edit a zone by {zone_id} (PATCH). e.g. pause/unpause.

Minimal body: {"paused":true}

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "paused": true
}
curl -X PUT 'https://api.endpointr.com/v1/cloudflare/zones/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "paused": true
}'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "paused": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"paused\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/cloudflare/zones/:id

Delete a zone by {zone_id}.

_Requires stored credentials: cloudflare (PUT /v1/credentials/cloudflare)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/cloudflare/zones/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cloudflare/zones/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cloudflare/zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Crawlers

AI Page Extractor

GET/v1/crawlers/ai-page-extractor?url=https%3A%2F%2Fexample.com&type=BUSINESS_SUMMARY

Same as POST but via query string.

Required query: url, type.

Minimal query: {"url":"https://example.com","type":"BUSINESS_SUMMARY"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
typeBUSINESS_SUMMARY
curl -X GET 'https://api.endpointr.com/v1/crawlers/ai-page-extractor?url=https%3A%2F%2Fexample.com&type=BUSINESS_SUMMARY' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/ai-page-extractor?url=https%3A%2F%2Fexample.com&type=BUSINESS_SUMMARY', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/ai-page-extractor?url=https%3A%2F%2Fexample.com&type=BUSINESS_SUMMARY');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/ai-page-extractor

Read a page and answer ONE structured question about the business behind it, named by type. Judgment-only — for phone numbers, emails, logos, socials or CMS use the deterministic sibling crawlers instead, which are faster and cannot be wrong.

Params. url (required). type (required, see below). geoCode optional ISO-2, forwarded to the fetch proxy. icp required for CONTENT_ICP_ALIGNMENT only.

type —
- BUSINESS_SUMMARY — what they sell and to whom.
- BUSINESS_MODEL — ECOMMERCE|LOCAL_SERVICE|B2B_SERVICE|PUBLISHER_AFFILIATE|SAAS|MARKETPLACE|BRANDING_ONLY. Fetch this first when building keywords — it decides the shape of everything downstream, and for PUBLISHER_AFFILIATE the money term is legitimately informational, which inverts the usual intent filter.
- SERVICES_OFFERED — what they sell, in the site's own wording, unnormalised.
- MONEY_KEYWORD_CANDIDATES — what a ready-to-buy customer would type. Hypotheses only.
- SERVICE_AREA — {country, area, cities}, each independently nullable.
- ICP — the buyer the site appears built for. Designed to be shown back to the owner as a question.
- CONTENT_ICP_ALIGNMENT — scores the page against a supplied icp. The only type taking an input.
- VALUE_PROPOSITION — verbatim or unknown; never composed.
- INDUSTRY_CLASSIFICATION — GICS, deepest verifiable level, null below.
- PAGE_LANGUAGE — BCP-47.

status — the field to branch on:
- ok — answered. See data.
- fetch_failed — the page was never read. NOT the same as an empty answer — the question is unanswered, and data is null rather than a shrug. Bot interstitials are caught here rather than being extracted from.
- rejected_untraceable — the model returned a derived value that could not name what it was derived from. Discarded rather than downgraded; violations[] lists the paths.
- unparseable — no JSON object came back.

Verification is per field, not per response. Each carries verification: quoted (page says it, exact text in evidence) · derived (inferred; derived_from names the inputs) · unknown (not establishable). Only quoted is safe to reproduce verbatim to a customer. unknown is a correct answer, not a failure — a thin page that does not say where a business operates returns null rather than a plausible town.

validated is always false. Nothing here has been checked against anything outside the page itself. Keyword candidates in particular must pass real search-volume and search-intent checks before reaching any customer-facing document.

fetch.rendered — false means JS-injected content was never seen, so a thin answer may be the fetch rather than the site. Pair with prompt_version when caching: a prompt revision invalidates a stored answer.

Status codes. 201 ok · 400 missing/invalid url or type, or a missing icp. A site that cannot be fetched is a 201 with status: fetch_failed, never an error.

Required body: url, type.

Minimal body: {"url":"https://example.com","type":"BUSINESS_MODEL"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com",
    "type": "BUSINESS_MODEL"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/ai-page-extractor' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com",
    "type": "BUSINESS_MODEL"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/ai-page-extractor', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com",
      "type": "BUSINESS_MODEL"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/ai-page-extractor');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\",\n    \"type\": \"BUSINESS_MODEL\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CMP Detector

GET/v1/crawlers/cmp-crawler?url=https%3A%2F%2Fexample.com

Same as POST but via query string.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/cmp-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cmp-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cmp-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/cmp-crawler

Detect the Consent Management Platform (cookie banner) on a URL, and inventory the cookies set before any consent interaction.

Params. url (required). geoCode optional ISO-2 country, forwarded to the fetch proxy for geo-routed sites.

verdict — the field to branch on:
- identified — CMP named. See primary.
- tcf_unidentified — a TCF consent API is present but the vendor could not be named.
- generic_banner — a cookie banner exists but matches no known vendor.
- none — page read, no banner found.
- fetch_failed — the page was never read. NOT the same as none — the question is unanswered, and detected is null rather than false.

verdict_reliable — false when the verdict is none and the fetch was not JS-rendered. Most CMPs inject themselves with JavaScript, so a non-rendered none only means "no CMP in the initial HTML". Filter on this before treating none as "this site has no cookie banner".

primary — {slug, name, vendor, tcf_cmp_id, tcf, type, confidence, id_source}. type is saas | cms_plugin | oss | native. A decoded TC string (id_source: tcstring) is authoritative and outranks every HTML heuristic; it also names IAB-certified CMPs that have no web fingerprint at all. cmps[] holds every candidate, best first.

cookies — {total, tracker_count, trackers_before_consent, categories, complete, items[]}. Analytics/advertising cookies present here were set with no consent given. complete is always false: cookies planted by third-party iframes/pixels are not observable, so this is a floor, not a census. Cookie values are never returned — names, attributes and classification only.

fetch.cookie_capture — rendered (page's own jar, includes JS-set trackers) · set-cookie-only (response headers only) · unavailable. Without a rendering proxy configured, expect set-cookie-only.

Status codes. 201 ok · 400 missing/invalid url. A site that cannot be fetched is a 200/201 with verdict: fetch_failed, never an error.

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/cmp-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cmp-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cmp-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CMS Detector

GET/v1/crawlers/cms-crawler?url=https%3A%2F%2Fexample.com

Same as POST but via query string.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/cms-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cms-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cms-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/cms-crawler

Detect CMS used by the target URL.

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/cms-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cms-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cms-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR Crawler

GET/v1/crawlers/cvr-crawler?url=https%3A%2F%2Fexample.dk

GET variant.

Required query: url.

Minimal query: {"url":"https://example.dk"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.dk
curl -X GET 'https://api.endpointr.com/v1/crawlers/cvr-crawler?url=https%3A%2F%2Fexample.dk' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cvr-crawler?url=https%3A%2F%2Fexample.dk', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cvr-crawler?url=https%3A%2F%2Fexample.dk');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/cvr-crawler

Extract Danish CVR numbers.

Required body: url.

Minimal body: {"url":"https://example.dk"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.dk"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/cvr-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.dk"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/cvr-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.dk"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/cvr-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.dk\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Email Crawler

GET/v1/crawlers/email-crawler?url=https%3A%2F%2Fexample.com

GET variant.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/email-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/email-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/email-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/email-crawler

Extract email addresses from a page.

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/email-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/email-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/email-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/link-crawler

Extract same-host anchor links (up to 500).

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/link-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/link-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/link-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Logo Extractor

GET/v1/crawlers/logo-crawler?url=https%3A%2F%2Fexample.com

GET variant.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/logo-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/logo-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/logo-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/logo-crawler

Extract the logo URL from a page.

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/logo-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/logo-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/logo-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Phone Crawler

GET/v1/crawlers/phone-crawler?url=https%3A%2F%2Fexample.com

GET variant.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/phone-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/phone-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/phone-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/phone-crawler

Extract phone numbers (tel: + textual).

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/phone-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/phone-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/phone-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Social Profiles

GET/v1/crawlers/social-profiles-crawler?url=https%3A%2F%2Fexample.com

GET variant.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/crawlers/social-profiles-crawler?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/crawlers/social-profiles-crawler?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/social-profiles-crawler?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/crawlers/social-profiles-crawler

Extract social-media profile URLs.

Required body: url.

Minimal body: {"url":"https://example.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/crawlers/social-profiles-crawler' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/crawlers/social-profiles-crawler', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/crawlers/social-profiles-crawler');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Cvr

CVR — Account labels

GET/v1/cvr/account-labels

List the CVR credential labels configured for this customer, as {accounts: [...], default: bool}. One login may hold several CVR agreements, each stored as cvr:<label>. When more than one is configured, every other CVR tool REQUIRES an explicit account argument naming which to use — call this first to see the valid labels. default: true means an unlabelled cvr credential also exists.

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cvr/account-labels' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/account-labels', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/account-labels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Company lookup

GET/v1/cvr/company-lookup/:id

Retrieve detailed official Danish CVR information about a company using its 8-digit CVR number. Returns the full normalised record: name and previous names, start/end dates, status, company form, primary and secondary industries, address, phone/email/website, employee count, advertising protection (reklamebeskyttelse), registered management roles, and production units (P-numbers). Also returns owners, split into legal (the ejerregister — who directly holds the shares, often a holding company, each carrying its own cvr so the chain can be walked) and beneficial (reelle ejere — the natural persons who ultimately own or control the company), with capital and voting percentages where CVR records them. Ownership is personal data and is available on this single-company lookup only — never on the bulk listing tools. The {id} path segment is the CVR number.

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cvr/company-lookup/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/company-lookup/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/company-lookup/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Credential test

GET/v1/cvr/credential-test

Verify the stored CVR credentials by running one minimal authenticated query against the Danish CVR API. Returns {ok: true, indexed_companies} on success, or {ok: false, error, message} with a safe error code (e.g. CVR_AUTHENTICATION_FAILED, CVR_CREDENTIAL_NOT_FOUND) on failure. Never returns the credentials themselves.

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/cvr/credential-test' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/credential-test', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/credential-test');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Industry stats

GET/v1/cvr/industry-stats?from_date=2026-08-01&to_date=2026-08-12&company_forms=ApS&active_only=1&size=50

Count newly started Danish companies grouped by industry (branchekode) over a date range, so you can see which kinds of company are being registered most frequently. Returns {total_companies, industries: [{code, name, count}]}, largest first. from_date and to_date are inclusive YYYY-MM-DD. size caps how many industry buckets come back (default 50, max 500).

Required query: from_date, to_date.

Minimal query: {"from_date":"2026-08-01","to_date":"2026-08-12"}

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
from_date2026-08-01
to_date2026-08-12
company_formsApS
active_only1
size50
curl -X GET 'https://api.endpointr.com/v1/cvr/industry-stats?from_date=2026-08-01&to_date=2026-08-12&company_forms=ApS&active_only=1&size=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/industry-stats?from_date=2026-08-01&to_date=2026-08-12&company_forms=ApS&active_only=1&size=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/industry-stats?from_date=2026-08-01&to_date=2026-08-12&company_forms=ApS&active_only=1&size=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Lead finder

GET/v1/cvr/lead-finder?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&exclude_holding_companies=1&require_website=&limit=100&offset=0

Find newly registered Danish companies as sales leads: the new-company search plus first-stage lead filtering. All exclusions are deterministic and structured — exclude_holding_companies (default true) drops holding/investment industry codes, and dissolved/bankrupt/inactive companies are always excluded. require_website keeps only companies with a registered website. Each lead carries lead_signals (e.g. new_company, aps, website_available, phone_available), which are restatements of facts in the record, not scores. The response's excluded object states exactly what was filtered out.

Required query: from_date, to_date.

Minimal query: {"from_date":"2026-08-11","to_date":"2026-08-12"}

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
from_date2026-08-11
to_date2026-08-12
company_formsApS
industry_codes620100
postal_codes1000-2999
exclude_holding_companies1
require_website
limit100
offset0
curl -X GET 'https://api.endpointr.com/v1/cvr/lead-finder?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&exclude_holding_companies=1&require_website=&limit=100&offset=0' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/lead-finder?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&exclude_holding_companies=1&require_website=&limit=100&offset=0', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/lead-finder?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&exclude_holding_companies=1&require_website=&limit=100&offset=0');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — New companies

GET/v1/cvr/new-companies?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&active_only=1&limit=100&offset=0&cursor=

Search the official Danish CVR register for companies whose official company START DATE falls within a given date range. Useful for finding newly established Danish companies, lead generation, market research and company monitoring. from_date and to_date are inclusive YYYY-MM-DD dates in Danish (Europe/Copenhagen) local time. Optional filters: company_forms (e.g. ["ApS","A/S","ENK"] or the numeric CVR form codes), industry_codes (branchekode), postal_codes (exact "2100" or a range "1000-2999"), municipalities, active_only (default true — excludes dissolved/bankrupt companies). Page with limit (max 500) + offset, or pass back next_cursor as cursor for result sets deeper than 10000.

Required query: from_date, to_date.

Minimal query: {"from_date":"2026-08-11","to_date":"2026-08-12"}

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
from_date2026-08-11
to_date2026-08-12
company_formsApS
industry_codes620100
postal_codes1000-2999
municipalitiesKøbenhavn
active_only1
limit100
offset0
cursor
curl -X GET 'https://api.endpointr.com/v1/cvr/new-companies?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&active_only=1&limit=100&offset=0&cursor=' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/new-companies?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&active_only=1&limit=100&offset=0&cursor=', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/new-companies?from_date=2026-08-11&to_date=2026-08-12&company_forms=ApS&industry_codes=620100&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&active_only=1&limit=100&offset=0&cursor=');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Prospects

GET/v1/cvr/prospects?days=7&since=&purpose=outreach&channel=linkedin&company_forms=ApS&industry_codes=620100&exclude_industry_codes=642120&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&require_phone=&has_website=&limit=50&offset=0&cursor=

Find newly registered Danish companies that may lawfully be contacted for sales or marketing. Unlike endpointr_cvr_new_companies_query, which mirrors the register verbatim, this endpoint applies marketing-safety defaults: it excludes reklamebeskyttede companies (~74% of new registrations, and contacting them is unlawful), ceased and dissolved companies, holding companies (branche 642120, ~18%, never real prospects), and our own trade — reklamebureauer (731110), computerprogrammering (620100) and andre informationstjenester (639900) are competitors, not buyers.

Paginate with days or since, NOT by start date. The endpoint returns companies by when they first appeared in this dataset, which is the only reliable way to see each company exactly once — the register backfills for days after a company's official start date, so start-date windows silently miss 40-50% of new registrations. from_date/to_date switch to start-date semantics and exist only for backfill audits.

Every row carries a contactable object stating which channels are lawful for that specific company. Use it rather than inferring from phone or email. email is always false: Danish markedsføringsloven § 10 requires prior consent for electronic marketing to companies, and the presence of an email address in the register is not consent. Physical post and B2B phone calls are permitted for companies without reklamebeskyttelse.

excluded_counts reports how many candidates each default removed, so a short list is explainable from the response. Pass purpose: "research" to disable all filtering and see the register as-is.

Each row carries channel — the best lawful route to that company: linkedin when a decision maker was resolved to a profile, otherwise phone or postal. Filter with channel to pull one bucket at a time. contact.person is the registered Direktion (or beneficial owner) from CVR, which is who to approach: a company registered weeks ago has no gatekeeper. signals states buying signals as facts — note that no_website and generic_email are POSITIVE signals for web/SEO services, not disqualifiers, and the LinkedIn lookup keys off the person's name so a missing website never blocks it.

Minimal query: {"days":7,"purpose":"outreach","channel":"linkedin","company_forms":["ApS"],"industry_codes":["620100"],"exclude_industry_codes":["642120"],"postal_codes":["1000-2999"],"municipalities":["K\u00f8benhavn"],"require_phone":false,"limit":50,"offset":0}

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
days7
since
purposeoutreach
channellinkedin
company_formsApS
industry_codes620100
exclude_industry_codes642120
postal_codes1000-2999
municipalitiesKøbenhavn
require_phone
has_website
limit50
offset0
cursor
curl -X GET 'https://api.endpointr.com/v1/cvr/prospects?days=7&since=&purpose=outreach&channel=linkedin&company_forms=ApS&industry_codes=620100&exclude_industry_codes=642120&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&require_phone=&has_website=&limit=50&offset=0&cursor=' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/prospects?days=7&since=&purpose=outreach&channel=linkedin&company_forms=ApS&industry_codes=620100&exclude_industry_codes=642120&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&require_phone=&has_website=&limit=50&offset=0&cursor=', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/prospects?days=7&since=&purpose=outreach&channel=linkedin&company_forms=ApS&industry_codes=620100&exclude_industry_codes=642120&postal_codes=1000-2999&municipalities=K%C3%B8benhavn&require_phone=&has_website=&limit=50&offset=0&cursor=');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

CVR — Recent companies

GET/v1/cvr/recent-companies?days=1&company_forms=ApS&industry_codes=620100&active_only=1&limit=100&offset=0

Newly started Danish companies from the last days calendar days — the convenience form of the new-companies search for questions like "give me all Danish companies created during the last 24 hours". The date window is computed in Danish (Europe/Copenhagen) local time and echoed back as from_date/to_date, so you never have to work out the boundary yourself. days defaults to 1 (today) and may not exceed 365.

Minimal query: {"days":1,"company_forms":["ApS"],"industry_codes":["620100"],"active_only":true,"limit":100,"offset":0}

_Requires stored credentials: cvr (PUT /v1/credentials/cvr)._

AuthorizationBearer YOUR_JWT_TOKEN
days1
company_formsApS
industry_codes620100
active_only1
limit100
offset0
curl -X GET 'https://api.endpointr.com/v1/cvr/recent-companies?days=1&company_forms=ApS&industry_codes=620100&active_only=1&limit=100&offset=0' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/cvr/recent-companies?days=1&company_forms=ApS&industry_codes=620100&active_only=1&limit=100&offset=0', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/cvr/recent-companies?days=1&company_forms=ApS&industry_codes=620100&active_only=1&limit=100&offset=0');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Generals

CVR Lookup

GET/v1/generals/cvr?cvr=12345678&country=dk&agent=Endpointr

Look up a company by its registration number via cvrapi. Required: cvr — the registration number you want to look up (user-supplied; not from another tool). country is optional and defaults to dk; set it to no for a Norwegian org number. Optional agent (a caller label). Example call: {"query":{"cvr":"12345678"}}.

Required query: cvr.

Minimal query: {"cvr":"12345678"}

AuthorizationBearer YOUR_JWT_TOKEN
cvr12345678
countrydk
agentEndpointr
curl -X GET 'https://api.endpointr.com/v1/generals/cvr?cvr=12345678&country=dk&agent=Endpointr' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/cvr?cvr=12345678&country=dk&agent=Endpointr', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/cvr?cvr=12345678&country=dk&agent=Endpointr');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

DNS

GET/v1/generals/dns?ip=1.1.1.1&action=reverse

Reverse DNS lookup or IP validity check.

Required query: ip.

Minimal query: {"ip":"1.1.1.1"}

AuthorizationBearer YOUR_JWT_TOKEN
ip1.1.1.1
actionreverse
curl -X GET 'https://api.endpointr.com/v1/generals/dns?ip=1.1.1.1&action=reverse' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/dns?ip=1.1.1.1&action=reverse', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/dns?ip=1.1.1.1&action=reverse');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

IP Geo (ipregistry)

GET/v1/generals/ip?ip=1.1.1.1

IP metadata via ipregistry (requires credentials).

Required query: ip.

Minimal query: {"ip":"1.1.1.1"}

_Requires stored credentials: ipregistry (PUT /v1/credentials/ipregistry)._

AuthorizationBearer YOUR_JWT_TOKEN
ip1.1.1.1
curl -X GET 'https://api.endpointr.com/v1/generals/ip?ip=1.1.1.1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/ip?ip=1.1.1.1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/ip?ip=1.1.1.1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Redirect Inspector

GET/v1/generals/redirect?url=https%3A%2F%2Fexample.com&action=chain

action=has|chain|to; follows hops with SSRF re-validation at each step.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
actionchain
curl -X GET 'https://api.endpointr.com/v1/generals/redirect?url=https%3A%2F%2Fexample.com&action=chain' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/redirect?url=https%3A%2F%2Fexample.com&action=chain', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/redirect?url=https%3A%2F%2Fexample.com&action=chain');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Remote User-Agent

GET/v1/generals/remote

Returns a randomly weighted user-agent string.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/generals/remote' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/remote', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/remote');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/generals/remote

GetByParam operation.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/generals/remote' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/remote', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/remote');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

VAT Validator

GET/v1/generals/vat?countrycode=DK&vatno=12345678

Validate an EU VAT number via VIES. Both required: countrycode (ISO-2 member state, e.g. DK, DE) and vatno (the number WITHOUT the country prefix, digits/letters only). Returns validity plus the registered name/address when the member state exposes them. Example call: {"query":{"countrycode":"DK","vatno":"12345678"}}.

Required query: countrycode, vatno.

Minimal query: {"countrycode":"DK","vatno":"12345678"}

AuthorizationBearer YOUR_JWT_TOKEN
countrycodeDK
vatno12345678
curl -X GET 'https://api.endpointr.com/v1/generals/vat?countrycode=DK&vatno=12345678' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/generals/vat?countrycode=DK&vatno=12345678', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/generals/vat?countrycode=DK&vatno=12345678');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Google Analytics — Admin

GA4 Admin — Account Summaries

GET/v1/analytics/admin/ga4-account-summaries

Start here to discover what GA4 properties a customer has. One call returns every account AND every property beneath it, each with its human-readable displayName — no need to already know an account id (unlike Properties below, which requires one). pageSize/pageToken are forwarded.

Auth — hybrid (creds in the query, or vault). Scope: analytics.readonly.

Response. {data: {accountSummaries: [{account:"accounts/123", displayName, propertySummaries: [{property:"properties/456", displayName, propertyType}]}]}, refreshed_access_token?} — feed the numeric id from propertySummaries[].property into Reports/Realtime/Metadata/Data Streams as ?property=, and account into Properties when creating a new one.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-account-summaries' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-account-summaries', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-account-summaries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Accounts

GET/v1/analytics/admin/ga4-accounts

List the GA4 accounts the authenticated user can access (top of the account → property → data-stream tree). Prefer Account Summaries above when you just want to browse — it returns properties in the same call.

An account cannot be created through the API (Google requires a human to accept the Terms of Service), so this list is the ceiling: everything endpointr can configure hangs off one of these.

Auth — hybrid (creds in the query, or vault). Scope: analytics.readonly.

Response. {data: {accounts: [{name:"accounts/123", displayName, …}]}, refreshed_access_token?} — feed accounts/{id} into Properties as ?account=.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Custom Dimensions

GET/v1/analytics/admin/ga4-custom-dimensions/:id

Fetch one custom dimension. id = propertyId:dimensionId or the full resource name properties/123456/customDimensions/789.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-custom-dimensions?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

List the property's custom dimensions. Required: property.

Scope: analytics.readonly.

Minimal query: {"oauth_token":"{{google_oauth_token}}","property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-custom-dimensions

Register an event parameter (or user property) so it becomes queryable in reports. A parameter your tag sends without a matching custom dimension is collected and then invisible — this is the most commonly missed step in a tracking setup.

Required: property, parameterName (exactly as the tag sends it), displayName, scope (EVENT | USER | ITEM). Optional description, disallowAdsPersonalization (USER scope only).

Quota is per property (50 event-scoped / 25 user-scoped on a standard property) and dimensions cannot be deleted — only archived.

Archive instead of delete: POST {action:'archive', id:'propertyId:dimensionId'}.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","parameterName":"form_name","displayName":"Form name","scope":"EVENT"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "parameterName": "form_name",
    "displayName": "Form name",
    "scope": "EVENT"
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "parameterName": "form_name",
    "displayName": "Form name",
    "scope": "EVENT"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "parameterName": "form_name",
      "displayName": "Form name",
      "scope": "EVENT"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"parameterName\": \"form_name\",\n    \"displayName\": \"Form name\",\n    \"scope\": \"EVENT\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-custom-dimensions/:id

Update a custom dimension (PATCH) — displayName and description only; parameterName and scope are immutable. id = propertyId:dimensionId or the full resource name.

Scope: analytics.edit.

Minimal body: {"displayName":"Form name (v2)"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "displayName": "Form name (v2)"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "displayName": "Form name (v2)"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "displayName": "Form name (v2)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-dimensions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"displayName\": \"Form name (v2)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Custom Metrics

GET/v1/analytics/admin/ga4-custom-metrics/:id

Fetch one custom metric. id = propertyId:metricId or the full resource name properties/123456/customMetrics/789.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-custom-metrics?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

List the property's custom metrics. Required: property.

Scope: analytics.readonly.

Minimal query: {"oauth_token":"{{google_oauth_token}}","property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-custom-metrics

Register a numeric event parameter so it can be summed/averaged in reports — the numeric twin of a custom dimension.

Required: property, parameterName, displayName, measurementUnit (STANDARD | CURRENCY | FEET | METERS | KILOMETERS | MILES | MILLISECONDS | SECONDS | MINUTES | HOURS). scope is EVENT.

Like dimensions, metrics are archived, never deleted: POST {action:'archive', id:'propertyId:metricId'}.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","parameterName":"quote_value","displayName":"Quote value","scope":"EVENT","measurementUnit":"CURRENCY"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "parameterName": "quote_value",
    "displayName": "Quote value",
    "scope": "EVENT",
    "measurementUnit": "CURRENCY"
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "parameterName": "quote_value",
    "displayName": "Quote value",
    "scope": "EVENT",
    "measurementUnit": "CURRENCY"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "parameterName": "quote_value",
      "displayName": "Quote value",
      "scope": "EVENT",
      "measurementUnit": "CURRENCY"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"parameterName\": \"quote_value\",\n    \"displayName\": \"Quote value\",\n    \"scope\": \"EVENT\",\n    \"measurementUnit\": \"CURRENCY\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-custom-metrics/:id

Update a custom metric (PATCH) — displayName / description. id = propertyId:metricId or the full resource name.

Scope: analytics.edit.

Minimal body: {"displayName":"Quote value (DKK)"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "displayName": "Quote value (DKK)"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "displayName": "Quote value (DKK)"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "displayName": "Quote value (DKK)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-custom-metrics/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"displayName\": \"Quote value (DKK)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Data Retention

GET/v1/analytics/admin/ga4-data-retention/:id

Read the retention settings by property id (the same singleton as the list form). id = the numeric property id or the full properties/123456.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-data-retention?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

Read a property's data retention settings. A per-property singleton, addressed by the PROPERTY id. Required: property.

Scope: analytics.readonly.

Minimal query: {"oauth_token":"{{google_oauth_token}}","property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-data-retention?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-data-retention/:id

Set retention. id is the PROPERTY id (123456 or properties/123456) — the settings have no id of their own.

eventDataRetention: TWO_MONTHS | FOURTEEN_MONTHS on a standard property (360 adds TWENTY_FIVE_MONTHS | THIRTY_EIGHT_MONTHS | FIFTY_MONTHS). resetUserDataOnNewActivity: boolean.

GA4 creates every property at TWO_MONTHS, which silently caps explorations at ~60 days. Bumping it is part of standing a property up — and it is not retroactive, so it only helps from the day it is set.

Scope: analytics.edit.

Minimal body: {"eventDataRetention":"FOURTEEN_MONTHS","resetUserDataOnNewActivity":true}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "eventDataRetention": "FOURTEEN_MONTHS",
    "resetUserDataOnNewActivity": true
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "eventDataRetention": "FOURTEEN_MONTHS",
    "resetUserDataOnNewActivity": true
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "eventDataRetention": "FOURTEEN_MONTHS",
      "resetUserDataOnNewActivity": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-retention/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"eventDataRetention\": \"FOURTEEN_MONTHS\",\n    \"resetUserDataOnNewActivity\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Data Streams

GET/v1/analytics/admin/ga4-data-streams/:id

Fetch one data stream. id = propertyId:streamId or the full resource name properties/123456/dataStreams/789.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-data-streams?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

List the data streams (web / iOS / Android) under a property — where the measurement ID / firebase app id lives. Required: property (properties/123456 or bare 123456). pageSize/pageToken are forwarded.

Auth — hybrid. Scope: analytics.readonly.

Required query: property.

Minimal query: {"property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-data-streams?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-data-streams

Step 2 of standing tracking up — this is what mints the measurement ID. Create a data stream under a property.

Required: property, type (WEB_DATA_STREAM | ANDROID_APP_DATA_STREAM | IOS_APP_DATA_STREAM), displayName. A web stream also needs webStreamData: {defaultUri}.

The response carries webStreamData.measurementId (G-XXXXXXXXXX) — the value a Google tag, or a GTM googtag tag's tagId parameter, needs. You cannot choose it.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","type":"WEB_DATA_STREAM","displayName":"example.com","webStreamData":{"defaultUri":"https://example.com"}}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "type": "WEB_DATA_STREAM",
    "displayName": "example.com",
    "webStreamData": {
        "defaultUri": "https://example.com"
    }
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-data-streams' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "type": "WEB_DATA_STREAM",
    "displayName": "example.com",
    "webStreamData": {
        "defaultUri": "https://example.com"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "type": "WEB_DATA_STREAM",
      "displayName": "example.com",
      "webStreamData": {
          "defaultUri": "https://example.com"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"type\": \"WEB_DATA_STREAM\",\n    \"displayName\": \"example.com\",\n    \"webStreamData\": {\n        \"defaultUri\": \"https://example.com\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-data-streams/:id

Update a data stream (PATCH). displayName masks itself; changing the URL means naming the nested leaf explicitly:

{"update_mask":"webStreamData.defaultUri","webStreamData":{"defaultUri":"https://example.com"}}

id = propertyId:streamId or the full resource name.

Scope: analytics.edit.

Minimal body: {"displayName":"example.com (web)"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "displayName": "example.com (web)"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "displayName": "example.com (web)"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "displayName": "example.com (web)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"displayName\": \"example.com (web)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/analytics/admin/ga4-data-streams/:id

Delete a data stream. Hard delete — the measurement ID stops collecting immediately and is not reissued. id = propertyId:streamId or the full resource name.

Scope: analytics.edit.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-data-streams/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Enhanced Measurement (alpha)

GET/v1/analytics/admin/ga4-enhanced-measurement/:id

Read a WEB stream's enhanced-measurement settings — the events GA4 collects automatically with no tag work (scrolls, outbound clicks, site search, video, file downloads, form interactions).

id = propertyId:streamId or the full properties/123456/dataStreams/789 (the /enhancedMeasurementSettings leaf is appended for you).

v1alpha — this resource has no v1beta equivalent, so it does not carry the v1beta stability guarantee.

Scope: analytics.readonly.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-enhanced-measurement/:id

Toggle a WEB stream's automatic events (PATCH). id = propertyId:streamId or the full stream resource name.

Booleans: streamEnabled, scrollsEnabled, outboundClicksEnabled, siteSearchEnabled, videoEngagementEnabled, fileDownloadsEnabled, pageChangesEnabled, formInteractionsEnabled. Plus searchQueryParameter (comma-separated, default q,s,search,query,keyword) and uriQueryParameter.

streamEnabled: false switches the whole feature off for the stream — the individual toggles keep their values but stop collecting.

v1alpha. Scope: analytics.edit.

Minimal body: {"formInteractionsEnabled":true,"searchQueryParameter":"q,s,search,query,keyword"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "formInteractionsEnabled": true,
    "searchQueryParameter": "q,s,search,query,keyword"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "formInteractionsEnabled": true,
    "searchQueryParameter": "q,s,search,query,keyword"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "formInteractionsEnabled": true,
      "searchQueryParameter": "q,s,search,query,keyword"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-enhanced-measurement/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"formInteractionsEnabled\": true,\n    \"searchQueryParameter\": \"q,s,search,query,keyword\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-google-ads-links

Link the property to a Google Ads account — what lets GA4 key events be imported as Ads conversions and feeds Ads its remarketing audiences.

Required: property, customerId — the Google Ads customer id, 10 digits, no dashes (not an endpointr customer). Optional adsPersonalizationEnabled (default true).

Google only links accounts the grant administers on BOTH sides: a 403 here almost always means editor-on-GA4 but not admin-on-Ads.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","customerId":"{{google_ads_customer_id}}","adsPersonalizationEnabled":true}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "customerId": "{{google_ads_customer_id}}",
    "adsPersonalizationEnabled": true
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-google-ads-links' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "customerId": "{{google_ads_customer_id}}",
    "adsPersonalizationEnabled": true
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-google-ads-links', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "customerId": "{{google_ads_customer_id}}",
      "adsPersonalizationEnabled": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-google-ads-links');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"customerId\": \"{{google_ads_customer_id}}\",\n    \"adsPersonalizationEnabled\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Key Events (conversions)

GET/v1/analytics/admin/ga4-key-events/:id

Fetch one key event. id = propertyId:keyEventId or the full resource name properties/123456/keyEvents/789.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-key-events?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

List the property's key events — what the GA4 UI calls conversions (the API renamed conversionEvents → keyEvents). Required: property.

Scope: analytics.readonly.

Minimal query: {"oauth_token":"{{google_oauth_token}}","property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-key-events?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-key-events?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-key-events?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-key-events

Mark an event as a key event — the step that makes it optimisable (and, once the property is linked to Google Ads, importable as a conversion).

Required: property, eventName. Optional countingMethod (ONCE_PER_EVENT default | ONCE_PER_SESSION) and defaultValue: {numericValue, currencyCode} for events that carry no value of their own.

The event does not have to have fired yet — marking generate_lead before the tag exists is normal, and is how you configure measurement ahead of the site work.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","eventName":"generate_lead","countingMethod":"ONCE_PER_EVENT"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "eventName": "generate_lead",
    "countingMethod": "ONCE_PER_EVENT"
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-key-events' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "eventName": "generate_lead",
    "countingMethod": "ONCE_PER_EVENT"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-key-events', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "eventName": "generate_lead",
      "countingMethod": "ONCE_PER_EVENT"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-key-events');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"eventName\": \"generate_lead\",\n    \"countingMethod\": \"ONCE_PER_EVENT\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-key-events/:id

Update a key event (PATCH) — in practice countingMethod or defaultValue. id = propertyId:keyEventId or the full resource name.

Scope: analytics.edit.

Minimal body: {"countingMethod":"ONCE_PER_SESSION"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "countingMethod": "ONCE_PER_SESSION"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "countingMethod": "ONCE_PER_SESSION"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "countingMethod": "ONCE_PER_SESSION"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"countingMethod\": \"ONCE_PER_SESSION\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/analytics/admin/ga4-key-events/:id

Unmark a key event (the underlying event keeps being collected — it just stops counting as a conversion). id = propertyId:keyEventId or the full resource name.

Scope: analytics.edit.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-key-events/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Measurement Protocol Secrets

GET/v1/analytics/admin/ga4-measurement-protocol-secrets/:id

Fetch one secret. id = propertyId:streamId:secretId or the full resource name.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-measurement-protocol-secrets?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D&stream=%7B%7Bga4_stream_id%7D%7D

List a data stream's Measurement Protocol secrets. Required: BOTH property and stream (the numeric stream id from ga4-data-streams).

Scope: analytics.readonly.

Minimal query: {"oauth_token":"{{google_oauth_token}}","property":"{{ga4_property_id}}","stream":"{{ga4_stream_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
stream{{ga4_stream_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D&stream=%7B%7Bga4_stream_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D&stream=%7B%7Bga4_stream_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D&stream=%7B%7Bga4_stream_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-measurement-protocol-secrets

Mint a Measurement Protocol secret for a stream. The secret plus the stream's measurement ID are the credentials for sending events to GA4 server-side — offline conversions, backend purchases, anything the browser tag cannot see.

Required: property, stream, displayName. Google generates the value and returns it as secretValue; treat it like a password — anyone holding it can write events into the property.

Scope: analytics.edit.

Minimal body: {"property":"{{ga4_property_id}}","stream":"{{ga4_stream_id}}","displayName":"Server-side events"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "property": "{{ga4_property_id}}",
    "stream": "{{ga4_stream_id}}",
    "displayName": "Server-side events"
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "property": "{{ga4_property_id}}",
    "stream": "{{ga4_stream_id}}",
    "displayName": "Server-side events"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "property": "{{ga4_property_id}}",
      "stream": "{{ga4_stream_id}}",
      "displayName": "Server-side events"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"property\": \"{{ga4_property_id}}\",\n    \"stream\": \"{{ga4_stream_id}}\",\n    \"displayName\": \"Server-side events\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/analytics/admin/ga4-measurement-protocol-secrets/:id

Revoke a secret. id = propertyId:streamId:secretId or the full resource name.

Scope: analytics.edit.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-measurement-protocol-secrets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Admin — Properties

GET/v1/analytics/admin/ga4-properties/:id

Fetch a single property by its numeric id (the 123456 in properties/123456). Auth in the query (?oauth_token=… or refresh-triplet).

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/analytics/admin/ga4-properties?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&account=%7B%7Bga4_account_id%7D%7D

List the properties under a GA4 account. Required: account (accounts/123456 or a bare 123456) — the Admin API has no unfiltered property list, so this becomes a parent:accounts/{id} filter. Extra query params (e.g. pageSize, pageToken, showDeleted) are forwarded.

Auth — hybrid. Scope: analytics.readonly.

Required query: account.

Minimal query: {"account":"{{ga4_account_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
account{{ga4_account_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/admin/ga4-properties?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&account=%7B%7Bga4_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-properties?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&account=%7B%7Bga4_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-properties?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&account=%7B%7Bga4_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/analytics/admin/ga4-properties

Step 1 of standing tracking up. Create a property under an existing account.

Required: account (or parent, accounts/123456), displayName, timeZone (IANA, e.g. Europe/Copenhagen). Strongly recommended: currencyCode (ISO-4217 — GA4 defaults to USD and it cannot be changed retroactively for past data), industryCategory.

propertyType defaults to PROPERTY_TYPE_ORDINARY. The response's name (properties/123456) is the id every other GA4 tool takes.

Next: create a WEB data stream to mint the measurement ID.

Scope: analytics.edit.

Minimal body: {"account":"{{ga4_account_id}}","displayName":"Example.com \u2014 Web","timeZone":"Europe/Copenhagen","currencyCode":"DKK","industryCategory":"BUSINESS_AND_INDUSTRIAL_MARKETS"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account": "{{ga4_account_id}}",
    "displayName": "Example.com — Web",
    "timeZone": "Europe/Copenhagen",
    "currencyCode": "DKK",
    "industryCategory": "BUSINESS_AND_INDUSTRIAL_MARKETS"
}
curl -X POST 'https://api.endpointr.com/v1/analytics/admin/ga4-properties' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account": "{{ga4_account_id}}",
    "displayName": "Example.com — Web",
    "timeZone": "Europe/Copenhagen",
    "currencyCode": "DKK",
    "industryCategory": "BUSINESS_AND_INDUSTRIAL_MARKETS"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-properties', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account": "{{ga4_account_id}}",
      "displayName": "Example.com — Web",
      "timeZone": "Europe/Copenhagen",
      "currencyCode": "DKK",
      "industryCategory": "BUSINESS_AND_INDUSTRIAL_MARKETS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-properties');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account\": \"{{ga4_account_id}}\",\n    \"displayName\": \"Example.com — Web\",\n    \"timeZone\": \"Europe/Copenhagen\",\n    \"currencyCode\": \"DKK\",\n    \"industryCategory\": \"BUSINESS_AND_INDUSTRIAL_MARKETS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/analytics/admin/ga4-properties/:id

Update a property (PATCH). Send only the fields you're changing — updateMask is derived from the body's top-level keys, or pass update_mask explicitly (comma-separated) to override.

Updatable: displayName, timeZone, currencyCode, industryCategory. id = the numeric property id or the full properties/123456.

Scope: analytics.edit.

Minimal body: {"displayName":"Example.com \u2014 Web (2026)"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "displayName": "Example.com — Web (2026)"
}
curl -X PUT 'https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "displayName": "Example.com — Web (2026)"
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "displayName": "Example.com — Web (2026)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"displayName\": \"Example.com — Web (2026)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/analytics/admin/ga4-properties/:id

Move a property to the trash. Soft delete: GA4 keeps it for 63 days (restorable from the UI, still counting against the account's property quota) and then purges it permanently. id = the numeric property id or the full properties/123456.

Scope: analytics.edit.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/admin/ga4-properties/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Google Analytics — Data

GA4 Data — Metadata

GET/v1/analytics/data/ga4-metadata?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D

List the dimensions and metrics available for a property (including custom ones) so you can discover valid name values before building a report. Required: property.

Auth — hybrid (creds in the query). Scope: analytics.readonly.

Required query: property.

Minimal query: {"property":"{{ga4_property_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
property{{ga4_property_id}}
curl -X GET 'https://api.endpointr.com/v1/analytics/data/ga4-metadata?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/analytics/data/ga4-metadata?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/data/ga4-metadata?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&property=%7B%7Bga4_property_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Data — Realtime Report

POST/v1/analytics/data/ga4-realtime

Activity in the last 30 minutes for a property (no dateRanges; a narrower dimension/metric set than the standard report — e.g. metric activeUsers, dimensions country, deviceCategory, unifiedScreenName).

Required: property. Other body fields forward to properties/{id}:runRealtimeReport.

Auth — hybrid (see Run Report). Scope: analytics.readonly.

Required body: property.

Minimal body: {"property":"{{ga4_property_id}}"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "property": "{{ga4_property_id}}",
    "dimensions": [
        {
            "name": "country"
        }
    ],
    "metrics": [
        {
            "name": "activeUsers"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/analytics/data/ga4-realtime' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "property": "{{ga4_property_id}}",
    "dimensions": [
        {
            "name": "country"
        }
    ],
    "metrics": [
        {
            "name": "activeUsers"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/data/ga4-realtime', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "property": "{{ga4_property_id}}",
      "dimensions": [
          {
              "name": "country"
          }
      ],
      "metrics": [
          {
              "name": "activeUsers"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/data/ga4-realtime');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"property\": \"{{ga4_property_id}}\",\n    \"dimensions\": [\n        {\n            \"name\": \"country\"\n        }\n    ],\n    \"metrics\": [\n        {\n            \"name\": \"activeUsers\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GA4 Data — Run Report

POST/v1/analytics/data/ga4-reports

Run a GA4 report: metrics × dimensions over one or more dateRanges for a property.

Required: property, and a report definition (at minimum dateRanges + metrics). Body fields other than property (and the OAuth creds) are forwarded verbatim to properties/{id}:runReport.

Common fields: dimensions:[{name}], metrics:[{name}], dateRanges:[{startDate,endDate}] (dates YYYY-MM-DD or relative like 7daysAgo/today), dimensionFilter, metricFilter, orderBys, limit, offset.

Auth — hybrid. Store creds once via PUT /v1/credentials/google; or carry oauth_token / the refresh-triplet in the body. Scope: analytics.readonly.

Required body: property.

Minimal body: {"property":"{{ga4_property_id}}"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "property": "{{ga4_property_id}}",
    "dateRanges": [
        {
            "startDate": "28daysAgo",
            "endDate": "today"
        }
    ],
    "dimensions": [
        {
            "name": "country"
        }
    ],
    "metrics": [
        {
            "name": "activeUsers"
        },
        {
            "name": "sessions"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/analytics/data/ga4-reports' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "property": "{{ga4_property_id}}",
    "dateRanges": [
        {
            "startDate": "28daysAgo",
            "endDate": "today"
        }
    ],
    "dimensions": [
        {
            "name": "country"
        }
    ],
    "metrics": [
        {
            "name": "activeUsers"
        },
        {
            "name": "sessions"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/analytics/data/ga4-reports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "property": "{{ga4_property_id}}",
      "dateRanges": [
          {
              "startDate": "28daysAgo",
              "endDate": "today"
          }
      ],
      "dimensions": [
          {
              "name": "country"
          }
      ],
      "metrics": [
          {
              "name": "activeUsers"
          },
          {
              "name": "sessions"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/analytics/data/ga4-reports');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"property\": \"{{ga4_property_id}}\",\n    \"dateRanges\": [\n        {\n            \"startDate\": \"28daysAgo\",\n            \"endDate\": \"today\"\n        }\n    ],\n    \"dimensions\": [\n        {\n            \"name\": \"country\"\n        }\n    ],\n    \"metrics\": [\n        {\n            \"name\": \"activeUsers\"\n        },\n        {\n            \"name\": \"sessions\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Google Tag Manager

GTM — Accounts

GET/v1/tagmanager/gtm-accounts

Takes no parameters — start here. Lists every GTM account the authenticated grant reaches; each result's accountId is the account_id every other gtm-* tool needs.

Auth — hybrid. Store creds once via PUT /v1/credentials/google-tagmanager (or the admin page's Connect button) and send nothing; or carry oauth_token / the refresh-triplet per-request. Scope: tagmanager.readonly.

Response. {data: {account: [{accountId, name, path, …}]}, refreshed_access_token?}.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-accounts/:id

Fetch one account. id = the numeric accountId or the full path accounts/{id}.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Built-in Variables

GET/v1/tagmanager/gtm-built-in-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D

List the built-in variables currently ENABLED in a workspace (Page URL, Click Classes, …). Required: account_id, container_id, workspace_id.

Built-ins aren't CRUD upstream — they are toggles: create = enable, delete = disable.

Required query: account_id, container_id, workspace_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
workspace_id{{gtm_workspace_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-built-in-variables

ENABLE one or more built-in variables. Required: the three parent ids + type — one type string or an array.

Common types: pageUrl, pageHostname, pagePath, referrer, event, clickElement, clickClasses, clickId, clickUrl, clickText, formId, formUrl, errorMessage, scrollDepthThreshold, videoStatus, containerId, containerVersion, randomNumber, environmentName.

Required body: account_id, container_id, workspace_id, type.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","type":["pageUrl","clickClasses"]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "type": [
        "pageUrl",
        "clickClasses"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "type": [
        "pageUrl",
        "clickClasses"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "workspace_id": "{{gtm_workspace_id}}",
      "type": [
          "pageUrl",
          "clickClasses"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"workspace_id\": \"{{gtm_workspace_id}}\",\n    \"type\": [\n        \"pageUrl\",\n        \"clickClasses\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-built-in-variables/:id

DISABLE one built-in variable. id = accountId:containerId:workspaceId:type — the 4th part is the TYPE (built-ins have no per-variable id), e.g. 1:2:3:clickClasses.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-built-in-variables/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Containers

GET/v1/tagmanager/gtm-containers/:id

Fetch one container. id = accountId:containerId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-containers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-containers?account_id=%7B%7Bgtm_account_id%7D%7D

List the containers under an account (account_id required) — or resolve one from its public snippet id with destination_id=GTM-XXXXXXX (:lookup; no account needed — handy when all you have is the snippet on a page; tag_id looks up by a GA4 measurement id instead).

Each result's numeric containerId is the container_id the workspace/version/environment tools take; publicId is the GTM-XXXXXXX id the site installs.

Auth — hybrid (vault google-tagmanager, or creds in the query). Scope: tagmanager.readonly.

Minimal query: {"account_id":"{{gtm_account_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-containers?account_id=%7B%7Bgtm_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-containers?account_id=%7B%7Bgtm_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-containers?account_id=%7B%7Bgtm_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-containers

Create a container under an account. Required: account_id, name, usageContext (array of platforms: web | android | ios | server | amp).

Auth — hybrid. Scope: tagmanager.edit.containers.

Required body: account_id, name, usageContext.

Minimal body: {"account_id":"{{gtm_account_id}}","name":"example.com","usageContext":["web"]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "name": "example.com",
    "usageContext": [
        "web"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-containers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "name": "example.com",
    "usageContext": [
        "web"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-containers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "name": "example.com",
      "usageContext": [
          "web"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-containers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"name\": \"example.com\",\n    \"usageContext\": [\n        \"web\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-containers/:id

Update a container (full-resource PUT — GTM replaces what you send). id = accountId:containerId or the full path. Pass the container's fingerprint for optimistic concurrency.

Minimal body: {"name":"example.com (renamed)"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "example.com (renamed)"
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-containers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "example.com (renamed)"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "example.com (renamed)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"example.com (renamed)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-containers/:id

Delete a container. IRREVERSIBLE — the container, all its versions and workspaces are gone. id = accountId:containerId or the full path. Scope: tagmanager.delete.containers.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-containers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-containers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Environments

GET/v1/tagmanager/gtm-environments/:id

Fetch one environment. id = accountId:containerId:environmentId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-environments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-environments?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D

List a container's environments — the built-in Live/Latest rows plus custom preview/staging environments (each with its authorizationCode and preview link). Required: account_id, container_id.

Required query: account_id, container_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-environments?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-environments?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-environments?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-environments

Create a CUSTOM environment (staging/QA — its snippet serves whatever version you publish to it). Required: account_id, container_id, name. Optional: description, enableDebug, url (the site it fronts).

Required body: account_id, container_id, name.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","name":"Staging"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "name": "Staging",
    "enableDebug": true,
    "url": "https://staging.example.com"
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-environments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "name": "Staging",
    "enableDebug": true,
    "url": "https://staging.example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-environments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "name": "Staging",
      "enableDebug": true,
      "url": "https://staging.example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-environments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"name\": \"Staging\",\n    \"enableDebug\": true,\n    \"url\": \"https://staging.example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-environments/:id

Update an environment (full-resource PUT; pass fingerprint for concurrency). id = accountId:containerId:environmentId or the full path.

Minimal body: {"name":"Staging","enableDebug":true}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Staging",
    "enableDebug": true
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-environments/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Staging",
    "enableDebug": true
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Staging",
      "enableDebug": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Staging\",\n    \"enableDebug\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-environments/:id

Delete a CUSTOM environment (the built-in Live/Latest rows can't be deleted). id = accountId:containerId:environmentId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-environments/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-environments/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Folders

GET/v1/tagmanager/gtm-folders/:id

Fetch one folder. id = accountId:containerId:workspaceId:folderId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-folders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-folders?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D

List the folders in a workspace. Required: account_id, container_id, workspace_id. File a tag/trigger/variable into a folder by setting parentFolderId on the entity itself (via its update tool).

Required query: account_id, container_id, workspace_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
workspace_id{{gtm_workspace_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-folders?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-folders?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-folders?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-folders

Create a folder in a workspace. Required: the three parent ids + name.

Required body: account_id, container_id, workspace_id, name.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","name":"Analytics"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "Analytics"
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-folders' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "Analytics"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-folders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "workspace_id": "{{gtm_workspace_id}}",
      "name": "Analytics"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-folders');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"workspace_id\": \"{{gtm_workspace_id}}\",\n    \"name\": \"Analytics\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-folders/:id

Rename a folder (full-resource PUT). id = accountId:containerId:workspaceId:folderId or the full path.

Minimal body: {"name":"Analytics"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Analytics"
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-folders/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Analytics"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Analytics"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Analytics\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-folders/:id

Delete a folder (entities inside it survive, unfiled). id = accountId:containerId:workspaceId:folderId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-folders/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-folders/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Tags

GET/v1/tagmanager/gtm-tags/:id

Fetch one tag. id = accountId:containerId:workspaceId:tagId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-tags/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-tags?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D

List the tags in a workspace. Required: account_id, container_id, workspace_id.

Auth — hybrid. Scope: tagmanager.readonly.

Required query: account_id, container_id, workspace_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
workspace_id{{gtm_workspace_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-tags?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-tags?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-tags?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-tags

Create a tag in a workspace. Required: the three parent ids, name, type. Everything else is the GTM tag resource, forwarded verbatim.

Common types: googtag (Google tag — GA4 config), gaawe (GA4 event), html (custom HTML), img (custom image/pixel), awct (Google Ads conversion), sp (Google Ads remarketing).

Tag settings ride parameter ([{type:'template'|'boolean'|'integer'|'list'|'map', key, value}]); firing rules ride firingTriggerId (array of trigger ids from gtm-triggers — 2147479553 is the built-in All Pages trigger id).

Custom-HTML example:

{"account_id":"…","container_id":"…","workspace_id":"…","name":"Plausible","type":"html","parameter":[{"type":"template","key":"html","value":"<script defer data-domain=\"example.com\" src=\"https://plausible.io/js/script.js\"></script>"}],"firingTriggerId":["2147479553"]}

Required body: account_id, container_id, workspace_id, name, type.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","name":"GA4 Configuration","type":"googtag"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "GA4 Configuration",
    "type": "googtag",
    "parameter": [
        {
            "type": "template",
            "key": "tagId",
            "value": "G-XXXXXXXXXX"
        }
    ],
    "firingTriggerId": [
        "2147479553"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-tags' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "GA4 Configuration",
    "type": "googtag",
    "parameter": [
        {
            "type": "template",
            "key": "tagId",
            "value": "G-XXXXXXXXXX"
        }
    ],
    "firingTriggerId": [
        "2147479553"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-tags', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "workspace_id": "{{gtm_workspace_id}}",
      "name": "GA4 Configuration",
      "type": "googtag",
      "parameter": [
          {
              "type": "template",
              "key": "tagId",
              "value": "G-XXXXXXXXXX"
          }
      ],
      "firingTriggerId": [
          "2147479553"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-tags');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"workspace_id\": \"{{gtm_workspace_id}}\",\n    \"name\": \"GA4 Configuration\",\n    \"type\": \"googtag\",\n    \"parameter\": [\n        {\n            \"type\": \"template\",\n            \"key\": \"tagId\",\n            \"value\": \"G-XXXXXXXXXX\"\n        }\n    ],\n    \"firingTriggerId\": [\n        \"2147479553\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-tags/:id

Update a tag (full-resource PUT — send the complete tag, not a diff; read it first, mutate, PUT back with its fingerprint). id = accountId:containerId:workspaceId:tagId or the full path.

Minimal body: {"name":"GA4 Configuration","type":"googtag","parameter":[{"type":"template","key":"tagId","value":"G-XXXXXXXXXX"}]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "GA4 Configuration",
    "type": "googtag",
    "parameter": [
        {
            "type": "template",
            "key": "tagId",
            "value": "G-XXXXXXXXXX"
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-tags/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "GA4 Configuration",
    "type": "googtag",
    "parameter": [
        {
            "type": "template",
            "key": "tagId",
            "value": "G-XXXXXXXXXX"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "GA4 Configuration",
      "type": "googtag",
      "parameter": [
          {
              "type": "template",
              "key": "tagId",
              "value": "G-XXXXXXXXXX"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"GA4 Configuration\",\n    \"type\": \"googtag\",\n    \"parameter\": [\n        {\n            \"type\": \"template\",\n            \"key\": \"tagId\",\n            \"value\": \"G-XXXXXXXXXX\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-tags/:id

Delete a tag from the workspace. id = accountId:containerId:workspaceId:tagId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-tags/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-tags/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Triggers

GET/v1/tagmanager/gtm-triggers/:id

Fetch one trigger. id = accountId:containerId:workspaceId:triggerId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-triggers?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D

List the triggers in a workspace. Required: account_id, container_id, workspace_id. A trigger's triggerId is what tags reference in firingTriggerId.

Auth — hybrid. Scope: tagmanager.readonly.

Required query: account_id, container_id, workspace_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
workspace_id{{gtm_workspace_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-triggers?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-triggers?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-triggers?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-triggers

Create a trigger in a workspace. Required: the three parent ids, name, type.

Common types: pageview, domReady, windowLoaded, click, linkClick, formSubmission, customEvent, historyChange, timer, scrollDepth, elementVisibility, youTubeVideo.

Conditions ride filter / customEventFilter / autoEventFilter: [{type:'equals'|'contains'|'startsWith'|'cssSelector'|…, parameter:[{type:'template',key:'arg0',value:'{{Page URL}}'},{type:'template',key:'arg1',value:'/checkout'}]}].

Custom-event example:

{"account_id":"…","container_id":"…","workspace_id":"…","name":"Purchase event","type":"customEvent","customEventFilter":[{"type":"equals","parameter":[{"type":"template","key":"arg0","value":"{{_event}}"},{"type":"template","key":"arg1","value":"purchase"}]}]}

Required body: account_id, container_id, workspace_id, name, type.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","name":"Checkout pages","type":"pageview"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "Checkout pages",
    "type": "pageview",
    "filter": [
        {
            "type": "contains",
            "parameter": [
                {
                    "type": "template",
                    "key": "arg0",
                    "value": "{{Page URL}}"
                },
                {
                    "type": "template",
                    "key": "arg1",
                    "value": "/checkout"
                }
            ]
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-triggers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "Checkout pages",
    "type": "pageview",
    "filter": [
        {
            "type": "contains",
            "parameter": [
                {
                    "type": "template",
                    "key": "arg0",
                    "value": "{{Page URL}}"
                },
                {
                    "type": "template",
                    "key": "arg1",
                    "value": "/checkout"
                }
            ]
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-triggers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "workspace_id": "{{gtm_workspace_id}}",
      "name": "Checkout pages",
      "type": "pageview",
      "filter": [
          {
              "type": "contains",
              "parameter": [
                  {
                      "type": "template",
                      "key": "arg0",
                      "value": "{{Page URL}}"
                  },
                  {
                      "type": "template",
                      "key": "arg1",
                      "value": "/checkout"
                  }
              ]
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-triggers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"workspace_id\": \"{{gtm_workspace_id}}\",\n    \"name\": \"Checkout pages\",\n    \"type\": \"pageview\",\n    \"filter\": [\n        {\n            \"type\": \"contains\",\n            \"parameter\": [\n                {\n                    \"type\": \"template\",\n                    \"key\": \"arg0\",\n                    \"value\": \"{{Page URL}}\"\n                },\n                {\n                    \"type\": \"template\",\n                    \"key\": \"arg1\",\n                    \"value\": \"/checkout\"\n                }\n            ]\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-triggers/:id

Update a trigger (full-resource PUT; read → mutate → PUT back with fingerprint). id = accountId:containerId:workspaceId:triggerId or the full path.

Minimal body: {"name":"Checkout pages","type":"pageview"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Checkout pages",
    "type": "pageview"
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Checkout pages",
    "type": "pageview"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Checkout pages",
      "type": "pageview"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Checkout pages\",\n    \"type\": \"pageview\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-triggers/:id

Delete a trigger. Tags still referencing its id keep a dangling firingTriggerId. id = accountId:containerId:workspaceId:triggerId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-triggers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Variables

GET/v1/tagmanager/gtm-variables/:id

Fetch one variable. id = accountId:containerId:workspaceId:variableId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-variables/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D

List the USER-DEFINED variables in a workspace (the one-click built-ins live on gtm-built-in-variables). Required: account_id, container_id, workspace_id.

Auth — hybrid. Scope: tagmanager.readonly.

Required query: account_id, container_id, workspace_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
workspace_id{{gtm_workspace_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-variables?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D&workspace_id=%7B%7Bgtm_workspace_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-variables

Create a user-defined variable in a workspace. Required: the three parent ids, name, type.

Common types: v (data layer variable), c (constant), jsm (custom JavaScript), k (1st-party cookie), u (URL component), d (DOM element), remm (regex lookup), smm (lookup table).

Settings ride parameter — a data layer variable takes [{type:'template',key:'name',value:'ecommerce.value'},{type:'integer',key:'dataLayerVersion',value:'2'}]. Reference the variable from tags/triggers as {{Its Name}}.

Required body: account_id, container_id, workspace_id, name, type.

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","name":"DL - purchase value","type":"v"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "DL - purchase value",
    "type": "v",
    "parameter": [
        {
            "type": "template",
            "key": "name",
            "value": "ecommerce.value"
        },
        {
            "type": "integer",
            "key": "dataLayerVersion",
            "value": "2"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-variables' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "workspace_id": "{{gtm_workspace_id}}",
    "name": "DL - purchase value",
    "type": "v",
    "parameter": [
        {
            "type": "template",
            "key": "name",
            "value": "ecommerce.value"
        },
        {
            "type": "integer",
            "key": "dataLayerVersion",
            "value": "2"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-variables', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "workspace_id": "{{gtm_workspace_id}}",
      "name": "DL - purchase value",
      "type": "v",
      "parameter": [
          {
              "type": "template",
              "key": "name",
              "value": "ecommerce.value"
          },
          {
              "type": "integer",
              "key": "dataLayerVersion",
              "value": "2"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-variables');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"workspace_id\": \"{{gtm_workspace_id}}\",\n    \"name\": \"DL - purchase value\",\n    \"type\": \"v\",\n    \"parameter\": [\n        {\n            \"type\": \"template\",\n            \"key\": \"name\",\n            \"value\": \"ecommerce.value\"\n        },\n        {\n            \"type\": \"integer\",\n            \"key\": \"dataLayerVersion\",\n            \"value\": \"2\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-variables/:id

Update a variable (full-resource PUT; read → mutate → PUT back with fingerprint). id = accountId:containerId:workspaceId:variableId or the full path.

Minimal body: {"name":"DL - purchase value","type":"v","parameter":[{"type":"template","key":"name","value":"ecommerce.value"}]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "DL - purchase value",
    "type": "v",
    "parameter": [
        {
            "type": "template",
            "key": "name",
            "value": "ecommerce.value"
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-variables/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "DL - purchase value",
    "type": "v",
    "parameter": [
        {
            "type": "template",
            "key": "name",
            "value": "ecommerce.value"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "DL - purchase value",
      "type": "v",
      "parameter": [
          {
              "type": "template",
              "key": "name",
              "value": "ecommerce.value"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"DL - purchase value\",\n    \"type\": \"v\",\n    \"parameter\": [\n        {\n            \"type\": \"template\",\n            \"key\": \"name\",\n            \"value\": \"ecommerce.value\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-variables/:id

Delete a variable. id = accountId:containerId:workspaceId:variableId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-variables/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-variables/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Versions (publish)

GET/v1/tagmanager/gtm-versions/:id

Fetch one FULL version (every tag/trigger/variable snapshotted in it). id = accountId:containerId:versionId, or the specials accountId:containerId:live (the currently-published version) and accountId:containerId:latest (newest version's header), or a full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-versions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-versions?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D

List a container's version HEADERS (lightweight — id, name, counts; not the full contents). Required: account_id, container_id. Add includeDeleted=true for trashed versions.

Auth — hybrid. Scope: tagmanager.readonly.

Required query: account_id, container_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-versions?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-versions?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-versions?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-versions

Version lifecycle actions — action required (versions are CREATED from a workspace via gtm-workspaces create_version, not here):

  • publish — push the version LIVE on every page carrying the snippet: {action:'publish', account_id, container_id, version_id, fingerprint?}. THE deploy step.
  • undelete — restore a trashed version.
  • set_latest — mark a version as the container's "latest" (what new workspaces base on) without publishing it.

The version target is account_id + container_id + version_id, or the version's full path.

Required body: action.

Minimal body: {"action":"publish"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "publish",
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "version_id": "{{gtm_version_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-versions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "publish",
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "version_id": "{{gtm_version_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-versions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "publish",
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "version_id": "{{gtm_version_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-versions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"publish\",\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"version_id\": \"{{gtm_version_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-versions/:id

Edit a version's name / description (full-resource PUT; contents are immutable). id = accountId:containerId:versionId or the full path.

Minimal body: {"name":"GA4 rollout","description":"Added GA4 config + purchase event"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "GA4 rollout",
    "description": "Added GA4 config + purchase event"
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-versions/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "GA4 rollout",
    "description": "Added GA4 config + purchase event"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "GA4 rollout",
      "description": "Added GA4 config + purchase event"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"GA4 rollout\",\n    \"description\": \"Added GA4 config + purchase event\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-versions/:id

Trash a version (restorable via create {action:'undelete'} until GTM purges it). The LIVE version can't be deleted. id = accountId:containerId:versionId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-versions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-versions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

GTM — Workspaces

GET/v1/tagmanager/gtm-workspaces/:id

Fetch one workspace. id = accountId:containerId:workspaceId or the full GTM path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tagmanager/gtm-workspaces?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D

List the workspaces of a container (account_id + container_id required) — the editable draft layer every tag/trigger/variable write happens in. Every container has a "Default Workspace"; its workspaceId is what the tag/trigger/variable tools take as workspace_id.

Auth — hybrid. Scope: tagmanager.readonly.

Required query: account_id, container_id.

Minimal query: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}"}

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{gtm_account_id}}
container_id{{gtm_container_id}}
curl -X GET 'https://api.endpointr.com/v1/tagmanager/gtm-workspaces?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-workspaces?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-workspaces?account_id=%7B%7Bgtm_account_id%7D%7D&container_id=%7B%7Bgtm_container_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tagmanager/gtm-workspaces

Three calls fold onto POST via action:

1. No action — create a workspace: {account_id, container_id, name, description?}.
2. action:'create_version' — snapshot the workspace into an immutable container version (this is how EVERY publish starts): {action, account_id, container_id, workspace_id, name?, notes?}. The response's containerVersion.containerVersionId is what gtm-versions publish takes as version_id. The workspace is consumed (GTM deletes it on versioning).
3. action:'sync' — pull latest-version changes into a stale workspace before versioning: {action, account_id, container_id, workspace_id} (response lists any merge conflicts).

Auth — hybrid. Scopes: tagmanager.edit.containers (create/sync), tagmanager.edit.containerversions (create_version).

Create-version example:

{"action":"create_version","account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","workspace_id":"{{gtm_workspace_id}}","name":"GA4 rollout","notes":"Added GA4 config + purchase event"}

Minimal body: {"account_id":"{{gtm_account_id}}","container_id":"{{gtm_container_id}}","name":"agent-changes"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "name": "agent-changes"
}
curl -X POST 'https://api.endpointr.com/v1/tagmanager/gtm-workspaces' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{gtm_account_id}}",
    "container_id": "{{gtm_container_id}}",
    "name": "agent-changes"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-workspaces', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{gtm_account_id}}",
      "container_id": "{{gtm_container_id}}",
      "name": "agent-changes"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-workspaces');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{gtm_account_id}}\",\n    \"container_id\": \"{{gtm_container_id}}\",\n    \"name\": \"agent-changes\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tagmanager/gtm-workspaces/:id

Rename a workspace / edit its description (full-resource PUT). id = accountId:containerId:workspaceId or the full path.

Minimal body: {"name":"agent-changes","description":"Automated edits"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "agent-changes",
    "description": "Automated edits"
}
curl -X PUT 'https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "agent-changes",
    "description": "Automated edits"
}'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "agent-changes",
      "description": "Automated edits"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"agent-changes\",\n    \"description\": \"Automated edits\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tagmanager/gtm-workspaces/:id

Delete a workspace and every unversioned change in it. id = accountId:containerId:workspaceId or the full path.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tagmanager/gtm-workspaces/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Graphics

Combine

POST/v1/graphics/combine

Two modes: overlay (base + overlay + x,y + opacity) or mosaic (images[] + layout + gap).

Minimal body: {"base_image_base64":"<base64>","overlay_image_base64":"<base64>","x":0,"y":0,"opacity":1,"format":"png"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "base_image_base64": "<base64>",
    "overlay_image_base64": "<base64>",
    "x": 0,
    "y": 0,
    "opacity": 1,
    "format": "png"
}
curl -X POST 'https://api.endpointr.com/v1/graphics/combine' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "base_image_base64": "<base64>",
    "overlay_image_base64": "<base64>",
    "x": 0,
    "y": 0,
    "opacity": 1,
    "format": "png"
}'
const response = await fetch('https://api.endpointr.com/v1/graphics/combine', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "base_image_base64": "<base64>",
      "overlay_image_base64": "<base64>",
      "x": 0,
      "y": 0,
      "opacity": 1,
      "format": "png"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/combine');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"base_image_base64\": \"<base64>\",\n    \"overlay_image_base64\": \"<base64>\",\n    \"x\": 0,\n    \"y\": 0,\n    \"opacity\": 1,\n    \"format\": \"png\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Convert Format

POST/v1/graphics/convert

Convert image between png/jpeg/webp/gif. jpeg auto-flattens transparency.

Required body: image_base64, format.

Minimal body: {"image_base64":"<base64>","format":"jpeg"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "image_base64": "<base64>",
    "format": "jpeg",
    "quality": 85
}
curl -X POST 'https://api.endpointr.com/v1/graphics/convert' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "image_base64": "<base64>",
    "format": "jpeg",
    "quality": 85
}'
const response = await fetch('https://api.endpointr.com/v1/graphics/convert', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "image_base64": "<base64>",
      "format": "jpeg",
      "quality": 85
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/convert');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"image_base64\": \"<base64>\",\n    \"format\": \"jpeg\",\n    \"quality\": 85\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Crop

POST/v1/graphics/crop

Crop a rectangle from an image. Overflow is clamped.

Required body: image_base64, x, y, width, height.

Minimal body: {"image_base64":"<base64>","x":0,"y":0,"width":200,"height":200}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "image_base64": "<base64>",
    "x": 0,
    "y": 0,
    "width": 200,
    "height": 200,
    "format": "png"
}
curl -X POST 'https://api.endpointr.com/v1/graphics/crop' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "image_base64": "<base64>",
    "x": 0,
    "y": 0,
    "width": 200,
    "height": 200,
    "format": "png"
}'
const response = await fetch('https://api.endpointr.com/v1/graphics/crop', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "image_base64": "<base64>",
      "x": 0,
      "y": 0,
      "width": 200,
      "height": 200,
      "format": "png"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/crop');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"image_base64\": \"<base64>\",\n    \"x\": 0,\n    \"y\": 0,\n    \"width\": 200,\n    \"height\": 200,\n    \"format\": \"png\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Resize

POST/v1/graphics/resize

Resize an image. mode: fit | cover | stretch.

Required body: image_base64, width, height.

Minimal body: {"image_base64":"<base64>","width":300,"height":200}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "image_base64": "<base64>",
    "width": 300,
    "height": 200,
    "mode": "cover",
    "format": "png",
    "quality": 85
}
curl -X POST 'https://api.endpointr.com/v1/graphics/resize' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "image_base64": "<base64>",
    "width": 300,
    "height": 200,
    "mode": "cover",
    "format": "png",
    "quality": 85
}'
const response = await fetch('https://api.endpointr.com/v1/graphics/resize', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "image_base64": "<base64>",
      "width": 300,
      "height": 200,
      "mode": "cover",
      "format": "png",
      "quality": 85
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/resize');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"image_base64\": \"<base64>\",\n    \"width\": 300,\n    \"height\": 200,\n    \"mode\": \"cover\",\n    \"format\": \"png\",\n    \"quality\": 85\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sponsored Text

GET/v1/graphics/text2-img?text=Sponsored

GET variant.

Minimal query: {"text":"Sponsored"}

AuthorizationBearer YOUR_JWT_TOKEN
textSponsored
curl -X GET 'https://api.endpointr.com/v1/graphics/text2-img?text=Sponsored' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/graphics/text2-img?text=Sponsored', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/text2-img?text=Sponsored');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/graphics/text2-img

Generate a 200x25 sponsored-label PNG. Empty text picks a random default.

Minimal body: {"text":"Sponsored"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "text": "Sponsored"
}
curl -X POST 'https://api.endpointr.com/v1/graphics/text2-img' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "text": "Sponsored"
}'
const response = await fetch('https://api.endpointr.com/v1/graphics/text2-img', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "text": "Sponsored"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/graphics/text2-img');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"text\": \"Sponsored\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Images

Host Image (24h)

POST/v1/images/host

General-purpose image hosting. Stores an image on the Endpointr public uploads volume and returns a URL that's reachable from anywhere on the internet for up to 24 hours, then auto-deleted.

When to use this vs /v1/ai/upload. The AI uploader is built for the vision flow: 30-minute TTL, base64-only. This endpoint is the general-purpose variant — longer TTL, three input modes, ideal for shrinking AI prompts in a long agent session (host once, reference by URL on every turn instead of re-encoding the same kilobytes of base64 over and over).

Request — pick exactly one input mode:
- image_base64 — raw base64 (no data: URI prefix). Simplest path for MCP / JSON-RPC clients that can't send multipart.
- image_url — a public https URL we'll fetch and mirror. SSRF-validated — internal/private IPs are rejected as 400. Useful for re-hosting a one-shot Meta CDN URL that's about to expire.
- file — multipart file=@path/to.png upload. HTTP-only (can't be expressed over MCP); cleanest for curl --form from a shell.
- format? — png | jpeg | webp | gif. Optional — magic bytes are sniffed and trusted over a caller hint.

Limits. Max raw size 25 MB. Allowed formats above.

Response. {url, mime, format, bytes, expires_in: 86400, expires_at: <iso8601>}. The 32-hex token in the URL has 128 bits of entropy; URL is the secret (no auth on download — necessary because vision providers / Meta / etc. can't carry your JWT).

TTL. 24 hours minimum. An hourly cron in the backup container deletes files older than 24 hours — effective lifetime is 24-25 hours. After that the URL 404s.

Other example bodies.

Mirror a remote image:

{"image_url":"https://example.com/cat.jpg"}

Curl with a local file (HTTP only, won't work over MCP):

curl -X POST {{baseUrl}}/v1/images/host \
  -H "Authorization: Bearer {{token}}" \
  -F file=@/path/to/cat.jpg

Minimal body: {"image_base64":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=","format":"png"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "format": "png"
}
curl -X POST 'https://api.endpointr.com/v1/images/host' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "format": "png"
}'
const response = await fetch('https://api.endpointr.com/v1/images/host', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
      "format": "png"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/images/host');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"image_base64\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=\",\n    \"format\": \"png\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Linkbuilding

Easifynow — Account

GET/v1/linkbuilding/easifynow-account

Start here — takes no parameters. Credits left (credits.regular / credits.premium), submission stats and link statistics. Proves the stored Easifynow login works; check credits before a publishing run.

Auth: vault easifynow {email, password}.

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-account' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-account', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-account');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Easifynow — Articles

GET/v1/linkbuilding/easifynow-articles

Takes no parameters. Submission history, newest first: id, post_title, app_status (pending|approved|rejected|published|error|…), wp_post_url (the live article), domain_url, error_message. Filter with the query tool.

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-articles' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-articles', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-articles');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/linkbuilding/easifynow-articles/:id

One submission by its id (the submission_id a publish returned): app_status, wp_post_url once live, link_url/anchor_text, error_message, and updated_at (needed by action:'update', fetched for you when omitted).

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/linkbuilding/easifynow-articles?status=published&limit=20

Submission history with filters. All optional: status (pending|approved|rejected|published|error), domain_id, submission_type, page, limit, sort_by, sort_direction.

Minimal query: {"status":"published","limit":20}

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
statuspublished
limit20
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-articles?status=published&limit=20' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-articles?status=published&limit=20', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-articles?status=published&limit=20');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/linkbuilding/easifynow-articles

Publishes an article on a public website and spends one Easifynow credit. Confirm the domain, title and links with the user before calling without dry_run.

Required: domain_id (from easifynow-pick / easifynow-domains), selected_category_id (a wp_category_id from easifynow-domain-categories), title, content (HTML), and a featured image: image_url (public https URL, fetched for you) or featured_image_base64 (data URI or base64). The image must be at least 800x400 px; it is recompressed under 1 MB when needed. Optional: selected_category_name, publication_timestamp (YYYY-MM-DD HH:MM:SS, Danish time; past or future; omit to publish now).

Easifynow's quality gate: at least 500 words (HTML and hidden text excluded), at most 3 external links each with anchor text, no script/iframe/event handlers, no hidden text, language matching the domain (a .dk domain needs Danish), no duplicate content, no keyword stuffing. Word count, links, tags and hidden text are checked locally first (PREFLIGHT_FAILED, nothing sent); the rest by Easifynow (422 CONTENT_REJECTED listing every rule).

dry_run: true runs the same checks plus Easifynow's own validation without publishing or spending a credit. Do this first.

Change a published article: {action:'update', id, title?, content?, post_date?} (queued; updated_at is read for you).

Returns data.submission_id; poll the get tool for app_status and wp_post_url.

Minimal body: {"domain_id":123,"selected_category_id":5,"title":"Title","content":"<p>\u2026</p>","image_url":"https://images.example.com/header.jpg","dry_run":true}

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "domain_id": 123,
    "selected_category_id": 5,
    "selected_category_name": "Bolig",
    "title": "Sådan vælger du det rigtige tag til dit hus",
    "content": "<p>… at least 500 words, at most 3 external links …</p>",
    "image_url": "https://images.example.com/header.jpg",
    "dry_run": true
}
curl -X POST 'https://api.endpointr.com/v1/linkbuilding/easifynow-articles' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "domain_id": 123,
    "selected_category_id": 5,
    "selected_category_name": "Bolig",
    "title": "Sådan vælger du det rigtige tag til dit hus",
    "content": "<p>… at least 500 words, at most 3 external links …</p>",
    "image_url": "https://images.example.com/header.jpg",
    "dry_run": true
}'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-articles', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "domain_id": 123,
      "selected_category_id": 5,
      "selected_category_name": "Bolig",
      "title": "Sådan vælger du det rigtige tag til dit hus",
      "content": "<p>… at least 500 words, at most 3 external links …</p>",
      "image_url": "https://images.example.com/header.jpg",
      "dry_run": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-articles');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"domain_id\": 123,\n    \"selected_category_id\": 5,\n    \"selected_category_name\": \"Bolig\",\n    \"title\": \"Sådan vælger du det rigtige tag til dit hus\",\n    \"content\": \"<p>… at least 500 words, at most 3 external links …</p>\",\n    \"image_url\": \"https://images.example.com/header.jpg\",\n    \"dry_run\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/linkbuilding/easifynow-articles/:id

Move a published article to the trash on the publisher site, by submission id (queued).

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-articles/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Easifynow — Domain categories

GET/v1/linkbuilding/easifynow-domain-categories/:id

A domain's active WordPress categories, by numeric domain id. Pick the category that fits the article; its wp_category_id is the selected_category_id and category_name the selected_category_name an article publish needs.

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-domain-categories/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-domain-categories/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-domain-categories/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Easifynow — Domains

GET/v1/linkbuilding/easifynow-domains

Takes no parameters. The first 200 publisher domains, sorted by traffic. Each row: id (the domain_id to publish on), navn, domain_url, dr, trafik (monthly traffic), backlinks, ref_domains, premium, tld, id_category. Response also carries submission_access (which submission types the account may use) and pagination. Use the query tool to filter, or easifynow-pick to have domains chosen for a target site.

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-domains' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-domains', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-domains');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/linkbuilding/easifynow-domains?tld=dk&exclude_linking_to=example.com&sort_by=trafik

Search the publisher domain catalogue. All filters optional.

Filters: tld (country code, e.g. dk; or tld_id), id_category, submission_type (regular|premium), search (name/URL), exclude_linking_to (a client site: drop domains that already link to it), only_linking_to, include_link_info (bool: outbound link targets per domain), sort_by (trafik|dr|ref_domains|backlinks|id, default trafik), sort_direction (DESC|ASC), limit (max 500, default 200), page.

Minimal query: {"tld":"dk","exclude_linking_to":"example.com","sort_by":"trafik"}

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
tlddk
exclude_linking_toexample.com
sort_bytrafik
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-domains?tld=dk&exclude_linking_to=example.com&sort_by=trafik' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-domains?tld=dk&exclude_linking_to=example.com&sort_by=trafik', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-domains?tld=dk&exclude_linking_to=example.com&sort_by=trafik');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Easifynow — Pick domains

GET/v1/linkbuilding/easifynow-pick?target=example.com&tld=dk&count=5

Pick the best publisher domains for one client site. target is required (the site the link will point to, e.g. example.com).

Domains that already link to the target are excluded. The rest are ranked on real traffic first, referring domains next, DR last; a domain whose DR is not backed by traffic (under 5 visits per DR point) is dropped as a link-farm signal. Each pick carries its numbers and a one-line reason; rejected counts what was filtered out and why.

Optional: count (default 5, max 50), tld (e.g. dk; the article must be in that language), id_category, submission_type (regular|premium), premium (bool), min_traffic (default 100), min_dr, max_dr, exclude_domain_ids (comma list, e.g. domains already used for this client).

Required query: target.

Minimal query: {"target":"example.com"}

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
targetexample.com
tlddk
count5
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-pick?target=example.com&tld=dk&count=5' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-pick?target=example.com&tld=dk&count=5', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-pick?target=example.com&tld=dk&count=5');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Easifynow — TLDs

GET/v1/linkbuilding/easifynow-tlds

Takes no parameters. The country TLDs the account can publish on (tld like .dk, is_available, access_status). The domain and pick tools take the code directly as tld (e.g. dk).

_Requires stored credentials: easifynow (PUT /v1/credentials/easifynow)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/linkbuilding/easifynow-tlds' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/linkbuilding/easifynow-tlds', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/linkbuilding/easifynow-tlds');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Mail

Mailchimp

GET/v1/mail/mailchimp

List configured Mailchimp audiences.

_Requires stored credentials: mailchimp (PUT /v1/credentials/mailchimp)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/mailchimp' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/mailchimp', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/mailchimp');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/mail/mailchimp

Subscribe an email to a list.

Required body: email.

Minimal body: {"email":"user@example.com"}

_Requires stored credentials: mailchimp (PUT /v1/credentials/mailchimp)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "email": "user@example.com",
    "list_id": "abc123",
    "merge_fields": {
        "FNAME": "Alice"
    }
}
curl -X POST 'https://api.endpointr.com/v1/mail/mailchimp' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "email": "user@example.com",
    "list_id": "abc123",
    "merge_fields": {
        "FNAME": "Alice"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/mail/mailchimp', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "email": "user@example.com",
      "list_id": "abc123",
      "merge_fields": {
          "FNAME": "Alice"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/mailchimp');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"email\": \"user@example.com\",\n    \"list_id\": \"abc123\",\n    \"merge_fields\": {\n        \"FNAME\": \"Alice\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/mail/mailchimp/:id

Update a member (id = email).

Minimal body: {"list_id":"abc123","merge_fields":{"FNAME":"Bob"}}

_Requires stored credentials: mailchimp (PUT /v1/credentials/mailchimp)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "list_id": "abc123",
    "merge_fields": {
        "FNAME": "Bob"
    }
}
curl -X PUT 'https://api.endpointr.com/v1/mail/mailchimp/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "list_id": "abc123",
    "merge_fields": {
        "FNAME": "Bob"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/mail/mailchimp/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "list_id": "abc123",
      "merge_fields": {
          "FNAME": "Bob"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/mailchimp/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"list_id\": \"abc123\",\n    \"merge_fields\": {\n        \"FNAME\": \"Bob\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/mail/mailchimp/:id?list_id=abc123

Remove a subscriber (id = email, list_id in query).

Minimal query: {"list_id":"abc123"}

_Requires stored credentials: mailchimp (PUT /v1/credentials/mailchimp)._

AuthorizationBearer YOUR_JWT_TOKEN
list_idabc123
curl -X DELETE 'https://api.endpointr.com/v1/mail/mailchimp/:id?list_id=abc123' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/mailchimp/:id?list_id=abc123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/mailchimp/:id?list_id=abc123');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Contact books

GET/v1/mail/missive-contact-books

Takes no parameters. Lists the contact books the token can access. Each result's id is the contact_book the contacts query/create tools require (Missive scopes contacts to a book).

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-contact-books' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-contact-books', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-contact-books');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Contacts

GET/v1/mail/missive-contacts/:id

Fetch a single contact (full memberships + infos). Auth: vault default, OR ?api_key={{missive_api_key}}.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-contacts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-contacts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-contacts?contact_book=%7B%7Bmissive_contact_book%7D%7D&limit=50

List contacts. Missive scopes contacts to a contact book, so contact_book is required — get it from the contact-books list tool (takes no parameters).

Optional: search (matches name + email), order (asc | desc), limit, offset, modified_since (unix), include_deleted (bool).

Auth — vault default.

Auth — per-request passthrough:

?contact_book={{missive_contact_book}}&limit=50&api_key={{missive_api_key}}

Other example queries.

Full-text search for an email or name fragment:

?contact_book={{missive_contact_book}}&search=alice%40example.com

Delta sync since last poll:

?contact_book={{missive_contact_book}}&modified_since=1764460800&include_deleted=true

Required query: contact_book.

Minimal query: {"contact_book":"{{missive_contact_book}}"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
contact_book{{missive_contact_book}}
limit50
curl -X GET 'https://api.endpointr.com/v1/mail/missive-contacts?contact_book=%7B%7Bmissive_contact_book%7D%7D&limit=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-contacts?contact_book=%7B%7Bmissive_contact_book%7D%7D&limit=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-contacts?contact_book=%7B%7Bmissive_contact_book%7D%7D&limit=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/mail/missive-contacts

Create one or more contacts.

Body shape. Pass either:
- a single contact object — we wrap it into Missive's {contacts: [...]} envelope, or
- a contacts array — we forward as-is.

Each entry needs at least contact_book.

Auth — vault default (canonical body above).

Auth — per-request passthrough:

{
  "api_key":"{{missive_api_key}}",
  "contact_book":"{{missive_contact_book}}",
  "first_name":"Alice",
  "last_name":"Doe",
  "infos":[{"kind":"email","value":"alice@example.com"}]
}

Other example bodies.

Batch import (array form):

{
  "contacts":[
    {"contact_book":"{{missive_contact_book}}","first_name":"Alice","last_name":"Doe","infos":[{"kind":"email","value":"alice@example.com"}]},
    {"contact_book":"{{missive_contact_book}}","first_name":"Bob", "last_name":"Roe","infos":[{"kind":"email","value":"bob@example.com"}]}
  ]
}

With multiple infos and a starred flag:

{
  "contact_book":"{{missive_contact_book}}",
  "first_name":"Alice",
  "last_name":"Doe",
  "starred":true,
  "infos":[
    {"kind":"email","value":"alice@example.com","label":"work"},
    {"kind":"phone","value":"+15555550100","label":"mobile"}
  ]
}

Required body: contact_book.

Minimal body: {"contact_book":"{{missive_contact_book}}"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "contact_book": "{{missive_contact_book}}",
    "first_name": "Alice",
    "last_name": "Doe",
    "infos": [
        {
            "kind": "email",
            "value": "alice@example.com"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/mail/missive-contacts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "contact_book": "{{missive_contact_book}}",
    "first_name": "Alice",
    "last_name": "Doe",
    "infos": [
        {
            "kind": "email",
            "value": "alice@example.com"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "contact_book": "{{missive_contact_book}}",
      "first_name": "Alice",
      "last_name": "Doe",
      "infos": [
          {
              "kind": "email",
              "value": "alice@example.com"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-contacts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"contact_book\": \"{{missive_contact_book}}\",\n    \"first_name\": \"Alice\",\n    \"last_name\": \"Doe\",\n    \"infos\": [\n        {\n            \"kind\": \"email\",\n            \"value\": \"alice@example.com\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/mail/missive-contacts/:id

Update a single contact (PATCH upstream). Body fields merge onto the existing contact.

Auth — vault default (canonical body above).

Auth — per-request passthrough:

{"api_key":"{{missive_api_key}}","first_name":"Alice (updated)"}

Other example bodies.

Add a phone number (replaces the infos array — fetch + merge client-side if you want to preserve existing entries):

{"infos":[{"kind":"email","value":"alice@example.com"},{"kind":"phone","value":"+15555550100"}]}

Star a contact:

{"starred":true}

Minimal body: {"first_name":"Alice (updated)"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "first_name": "Alice (updated)"
}
curl -X PUT 'https://api.endpointr.com/v1/mail/missive-contacts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "first_name": "Alice (updated)"
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-contacts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "first_name": "Alice (updated)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-contacts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"first_name\": \"Alice (updated)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Conversations

GET/v1/mail/missive-conversations

Takes no parameters — the conversation-id discovery entry point. Returns the 25 most recent conversations from the "All" mailbox (Missive requires a mailbox scope; this defaults it for you). Each result's id is the conversation id that messages/posts/drafts/tasks reference. Use the query tool for a different mailbox, filters, or pagination.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-conversations' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-conversations', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-conversations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-conversations/:id

Fetch a single conversation (with metadata). Auth: vault default, OR ?api_key={{missive_api_key}}.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-conversations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-conversations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-conversations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-conversations?limit=10&inbox=true

List conversations — the "last n mails" entry point.

limit (1-50; default 25) plus any combination of scope filters: inbox, all, assigned, closed, snoozed, flagged, trashed, junked, drafts, shared_label, team_inbox, team_closed, team_all, organization, email, domain, contact_organization. until is a unix timestamp for pagination (cursor on last_activity_at).

Auth — vault (canonical query above). Store creds once.

Auth — per-request passthrough. Add api_key to the query string:

?limit=10&inbox=true&api_key={{missive_api_key}}

Other example queries.

Last 25 conversations assigned to anyone:

?assigned=true&limit=25

Conversations from a specific email address (handy for support context):

?email=customer@example.com&all=true&limit=20

Paginate further back in time:

?inbox=true&limit=25&until=1761955200

Minimal query: {"limit":"10","inbox":"true"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
limit10
inboxtrue
curl -X GET 'https://api.endpointr.com/v1/mail/missive-conversations?limit=10&inbox=true' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-conversations?limit=10&inbox=true', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-conversations?limit=10&inbox=true');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Drafts

POST/v1/mail/missive-drafts

Create a draft (and optionally send it). Body is the inner shape of Missive's drafts object — we wrap it as {drafts: ...} upstream.

Required: from_field (sender) OR conversation (replying inside an existing thread).

Useful optional fields: subject, to_fields[], cc_fields[], bcc_fields[], body (HTML), attachments[], references[], send (bool), send_at (unix), close, add_to_inbox, add_to_team_inbox, add_users[], add_assignees[], add_shared_labels[], organization, team, account, quote_previous_message.

Auth — vault mode (canonical body above). Store the api_key once via PUT /v1/credentials/missive {api_key:...}; never send it again.

Auth — per-request passthrough. Add api_key to this body and skip the vault entry entirely:

{
  "api_key":"{{missive_api_key}}",
  "from_field":{"address":"me@example.com","name":"Me"},
  "to_fields":[{"address":"you@example.com","name":"You"}],
  "subject":"Hello from Endpointr",
  "body":"<p>Sent via Endpointr passthrough.</p>",
  "send":false
}

Other example bodies.

Reply inside an existing conversation (omits from_field):

{"conversation":"{{missive_conversation}}","body":"<p>Got it — circling back next week.</p>","send":true}

Send immediately and assign to a teammate:

{
  "from_field":{"address":"me@example.com","name":"Me"},
  "to_fields":[{"address":"you@example.com"}],
  "subject":"Welcome",
  "body":"<p>Hi!</p>",
  "send":true,
  "organization":"{{missive_organization}}",
  "add_assignees":["{{missive_user}}"]
}

Schedule for later (send_at is unix seconds):

{"from_field":{"address":"me@example.com"},"to_fields":[{"address":"you@example.com"}],"subject":"Reminder","body":"<p>Heads up</p>","send_at":1764547200}

Minimal body: {"from_field":{"address":"me@example.com","name":"Me"},"to_fields":[{"address":"you@example.com","name":"You"}],"subject":"Hello from Endpointr","body":"<p>This was created via the Missive REST API relay.</p>","send":false}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "from_field": {
        "address": "me@example.com",
        "name": "Me"
    },
    "to_fields": [
        {
            "address": "you@example.com",
            "name": "You"
        }
    ],
    "subject": "Hello from Endpointr",
    "body": "<p>This was created via the Missive REST API relay.</p>",
    "send": false
}
curl -X POST 'https://api.endpointr.com/v1/mail/missive-drafts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "from_field": {
        "address": "me@example.com",
        "name": "Me"
    },
    "to_fields": [
        {
            "address": "you@example.com",
            "name": "You"
        }
    ],
    "subject": "Hello from Endpointr",
    "body": "<p>This was created via the Missive REST API relay.</p>",
    "send": false
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-drafts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "from_field": {
          "address": "me@example.com",
          "name": "Me"
      },
      "to_fields": [
          {
              "address": "you@example.com",
              "name": "You"
          }
      ],
      "subject": "Hello from Endpointr",
      "body": "<p>This was created via the Missive REST API relay.</p>",
      "send": false
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-drafts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"from_field\": {\n        \"address\": \"me@example.com\",\n        \"name\": \"Me\"\n    },\n    \"to_fields\": [\n        {\n            \"address\": \"you@example.com\",\n            \"name\": \"You\"\n        }\n    ],\n    \"subject\": \"Hello from Endpointr\",\n    \"body\": \"<p>This was created via the Missive REST API relay.</p>\",\n    \"send\": false\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/mail/missive-drafts/:id

Delete a draft by id. The draft id comes from the create response (drafts.id) — Missive has no list-drafts endpoint (draft *conversations* can be found via the conversations query with drafts=true, but that yields conversation ids, not draft ids). Auth: vault by default, OR ?api_key={{missive_api_key}} for passthrough.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/mail/missive-drafts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-drafts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-drafts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Messages

GET/v1/mail/missive-messages/:id

Fetch a single message (full headers + body). Auth: vault default, OR ?api_key={{missive_api_key}}.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-messages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-messages/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-messages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-messages?conversation=%7B%7Bmissive_conversation%7D%7D&limit=20

Read messages. Missive has no global message-list endpoint, so this dispatches based on the query you send:

  • ?conversation=<id>&limit=… → list messages within a conversation
  • ?email_message_id=<rfc822> → look up by RFC-822 Message-ID header

Auth — vault default.

Auth — per-request passthrough:

?conversation={{missive_conversation}}&limit=20&api_key={{missive_api_key}}

Other example queries.

Look up a specific email by its Message-ID header (great for inbound webhook correlation):

?email_message_id=<CAEbf...@mail.gmail.com>

Minimal query: {"conversation":"{{missive_conversation}}","limit":"20"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
conversation{{missive_conversation}}
limit20
curl -X GET 'https://api.endpointr.com/v1/mail/missive-messages?conversation=%7B%7Bmissive_conversation%7D%7D&limit=20' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-messages?conversation=%7B%7Bmissive_conversation%7D%7D&limit=20', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-messages?conversation=%7B%7Bmissive_conversation%7D%7D&limit=20');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Organizations

GET/v1/mail/missive-organizations

Takes no parameters. Lists the organizations the token belongs to. Each result's id is the organization the conversations filter takes, the teams/users/shared-labels lists accept, and tasks/drafts routing uses.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-organizations' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-organizations', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-organizations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Posts (comments)

POST/v1/mail/missive-posts

Append a post — Missive's term for an internal comment or system message rendered inside a conversation. Useful for: writing internal notes from automations, surfacing third-party alerts (PagerDuty, Stripe, etc.) into the team's inbox, or annotating conversations with context.

Required:
- conversation (existing thread) OR references (find-or-create — Missive matches against email Message-IDs)
- notification — object with title + body. Missive uses this for the in-app notification card; *not* shown in the conversation body.
- One of text, markdown, or attachments — the actual post content.

Forwarded as-is: username (custom display name), add_users, add_assignees, add_to_inbox, add_to_team_inbox, close, organization, team.

Auth — vault default (canonical body above).

Auth — per-request passthrough:

{
  "api_key":"{{missive_api_key}}",
  "conversation":"{{missive_conversation}}",
  "notification":{"title":"New comment","body":"Posted from Endpointr"},
  "markdown":"Heads up — see the new attachment."
}

Other example bodies.

Find-or-create a conversation by Message-ID:

{
  "references":["<order-1234@orders.example.com>"],
  "notification":{"title":"Stripe alert","body":"Refund requested for charge ch_xxx"},
  "text":"Refund $42.50 was issued by Alice in Stripe. Original charge: ch_3PqXyz."
}

Markdown comment with assignees and team-inbox routing:

{
  "conversation":"{{missive_conversation}}",
  "notification":{"title":"Escalation","body":"Customer needs review"},
  "markdown":"**Escalating** — repeated 5xx since 14:02 UTC. Logs: https://...",
  "add_assignees":["{{missive_user}}"],
  "add_to_team_inbox":true,
  "team":"{{missive_team}}"
}

Required body: notification.

Minimal body: {"notification":{"title":"New comment","body":"Posted from Endpointr"}}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "conversation": "{{missive_conversation}}",
    "notification": {
        "title": "New comment",
        "body": "Posted from Endpointr"
    },
    "markdown": "Heads up — see the new attachment."
}
curl -X POST 'https://api.endpointr.com/v1/mail/missive-posts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "conversation": "{{missive_conversation}}",
    "notification": {
        "title": "New comment",
        "body": "Posted from Endpointr"
    },
    "markdown": "Heads up — see the new attachment."
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "conversation": "{{missive_conversation}}",
      "notification": {
          "title": "New comment",
          "body": "Posted from Endpointr"
      },
      "markdown": "Heads up — see the new attachment."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-posts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"conversation\": \"{{missive_conversation}}\",\n    \"notification\": {\n        \"title\": \"New comment\",\n        \"body\": \"Posted from Endpointr\"\n    },\n    \"markdown\": \"Heads up — see the new attachment.\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Shared labels

GET/v1/mail/missive-shared-labels

Takes no parameters. Lists all shared labels. Each result's id is the label id the conversations shared_label filter and drafts/posts add_shared_labels take.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-shared-labels' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-shared-labels', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-shared-labels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-shared-labels?organization=%7B%7Bmissive_organization%7D%7D

List shared labels filtered to one organization (id from the organizations list tool).

Minimal query: {"organization":"{{missive_organization}}"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
organization{{missive_organization}}
curl -X GET 'https://api.endpointr.com/v1/mail/missive-shared-labels?organization=%7B%7Bmissive_organization%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-shared-labels?organization=%7B%7Bmissive_organization%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-shared-labels?organization=%7B%7Bmissive_organization%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Tasks (conversation tasks)

POST/v1/mail/missive-tasks

Create a Missive task. Conversation- or organization-scoped — distinct from Google Tasks (/v1/tasks/google-tasks), which are personal to-dos on a Google account.

Required: title.

For standalone tasks also required: organization plus at least one of team, assignees, or add_users.

For sub-tasks (tasks attached to a specific conversation): subtask: true plus either conversation or references.

Auth — vault default (canonical body above).

Auth — per-request passthrough:

{
  "api_key":"{{missive_api_key}}",
  "title":"Follow up with customer",
  "organization":"{{missive_organization}}",
  "assignees":["{{missive_user}}"],
  "due_at":1764547200
}

Other example bodies.

Sub-task tied to a conversation:

{
  "title":"Send refund confirmation",
  "subtask":true,
  "conversation":"{{missive_conversation}}"
}

Team-assigned task with due date one week from now:

{
  "title":"Review Q2 numbers",
  "organization":"{{missive_organization}}",
  "team":"{{missive_team}}",
  "due_at":1765152000
}

Required body: title.

Minimal body: {"title":"Follow up with customer"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "title": "Follow up with customer",
    "organization": "{{missive_organization}}",
    "assignees": [
        "{{missive_user}}"
    ],
    "due_at": 1764547200
}
curl -X POST 'https://api.endpointr.com/v1/mail/missive-tasks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "title": "Follow up with customer",
    "organization": "{{missive_organization}}",
    "assignees": [
        "{{missive_user}}"
    ],
    "due_at": 1764547200
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-tasks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "title": "Follow up with customer",
      "organization": "{{missive_organization}}",
      "assignees": [
          "{{missive_user}}"
      ],
      "due_at": 1764547200
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-tasks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"title\": \"Follow up with customer\",\n    \"organization\": \"{{missive_organization}}\",\n    \"assignees\": [\n        \"{{missive_user}}\"\n    ],\n    \"due_at\": 1764547200\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/mail/missive-tasks/:id

Update a task (PATCH upstream). The task id comes from the create response — Missive has no list-tasks endpoint (upstream limitation), so keep the id from creation. Updatable fields: state (todo | in_progress | closed), title, due_at.

Auth — vault default (canonical body above).

Auth — per-request passthrough:

{"api_key":"{{missive_api_key}}","state":"closed"}

Other example bodies.

Mark in-progress and bump due date:

{"state":"in_progress","due_at":1765843200}

Rename:

{"title":"Follow up — pinged Alice"}

Minimal body: {"state":"in_progress"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "state": "in_progress"
}
curl -X PUT 'https://api.endpointr.com/v1/mail/missive-tasks/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "state": "in_progress"
}'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-tasks/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "state": "in_progress"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-tasks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"state\": \"in_progress\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/mail/missive-tasks/:id

Mark a task done. The task id comes from the create response (no list-tasks endpoint upstream). Missive has no real DELETE on tasks; this transitions to state: "closed" upstream. Auth: vault default, OR ?api_key={{missive_api_key}}.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/mail/missive-tasks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-tasks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-tasks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Teams

GET/v1/mail/missive-teams

Takes no parameters. Lists all teams. Each result's id is the team id the conversations filters (team_inbox/team_all/team_closed), tasks (team), and drafts routing take.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-teams' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-teams', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-teams');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-teams?organization=%7B%7Bmissive_organization%7D%7D

List teams filtered to one organization (id from the organizations list tool).

Minimal query: {"organization":"{{missive_organization}}"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
organization{{missive_organization}}
curl -X GET 'https://api.endpointr.com/v1/mail/missive-teams?organization=%7B%7Bmissive_organization%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-teams?organization=%7B%7Bmissive_organization%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-teams?organization=%7B%7Bmissive_organization%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Missive — Users

GET/v1/mail/missive-users

Takes no parameters. Lists all users. Each result's id is the user id that drafts (add_assignees) and tasks (assignees) reference.

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/mail/missive-users' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-users', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-users');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/mail/missive-users?organization=%7B%7Bmissive_organization%7D%7D

List users filtered to one organization (id from the organizations list tool).

Minimal query: {"organization":"{{missive_organization}}"}

_Requires stored credentials: missive (PUT /v1/credentials/missive)._

AuthorizationBearer YOUR_JWT_TOKEN
organization{{missive_organization}}
curl -X GET 'https://api.endpointr.com/v1/mail/missive-users?organization=%7B%7Bmissive_organization%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/mail/missive-users?organization=%7B%7Bmissive_organization%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/mail/missive-users?organization=%7B%7Bmissive_organization%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Meta Conversation

Channels (connected Pages / IG)

GET/v1/messaging/channels

List the Facebook Pages (and linked Instagram business accounts) the stored token can message through. Walks /me/accounts and caches each Page's access token encrypted in meta_msg_channels so later message/send/profile calls can use the correct Page token. Pass ?refresh=true to force re-discovery. Page tokens are never returned in the response.

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/channels' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/channels', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/channels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/messaging/channels/:id

Fetch one connected channel by Page id (discovers + caches if not seen yet).

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/channels/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/channels/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/channels/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Conversations

GET/v1/messaging/conversations/:id

Fetch one conversation by id. Local mirror first; ?refresh=true&page_id=<id> forces live.

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/conversations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/conversations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/conversations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/messaging/conversations?page_id=%3Cpage-id%3E&platform=messenger&refresh=false

List conversations (threads) on a Page.

?page_id=<id> is required. ?platform=messenger (default) or instagram. Reads from the local mirror by default; ?refresh=true forces a Graph round-trip and re-mirrors.

Response: {collection:[Conversation…], source:"local"|"live"}.

Required query: page_id.

Minimal query: {"page_id":"<page-id>"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
page_id<page-id>
platformmessenger
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/messaging/conversations?page_id=%3Cpage-id%3E&platform=messenger&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/conversations?page_id=%3Cpage-id%3E&platform=messenger&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/conversations?page_id=%3Cpage-id%3E&platform=messenger&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Messages (read + Send API)

GET/v1/messaging/messages/:id

Fetch one message by Meta message id. Pass ?page_id=<id> for the Page token.

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/messages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/messages/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/messaging/messages?conversation_id=%3Cconversation-id%3E&page_id=%3Cpage-id%3E&refresh=false

List messages in a conversation.

?conversation_id=<id> is required; pass ?page_id=<id> so the Page token resolves. ?refresh=true forces a live Graph pull. Inbound webhook messages also land in this same store.

Required query: conversation_id.

Minimal query: {"conversation_id":"<conversation-id>"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
conversation_id<conversation-id>
page_id<page-id>
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/messaging/messages?conversation_id=%3Cconversation-id%3E&page_id=%3Cpage-id%3E&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/messages?conversation_id=%3Cconversation-id%3E&page_id=%3Cpage-id%3E&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messages?conversation_id=%3Cconversation-id%3E&page_id=%3Cpage-id%3E&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/messaging/messages

Send a message via the Send API (POST /{page-id}/messages).

Required: page_id, recipient ({id:<psid>}) or recipient_id, and message.

Message shapes:
- Text: {message:{text:"hi"}}
- Attachment: {message:{attachment:{type:"image",payload:{url:"…",is_reusable:true}}}}
- Quick replies: {message:{text:"Pick",quick_replies:[…]}}
- Template: {message:{attachment:{type:"template",payload:{…}}}}

Optional: messaging_type (RESPONSE default | UPDATE | MESSAGE_TAG), tag (sets messaging_type=MESSAGE_TAG — needed outside the 24h window), platform (messenger|instagram).

Sender actions via the same POST: {action:'typing_on'|'typing_off'|'mark_seen', page_id, recipient_id}.

Sent messages are mirrored into the local store with direction:"out".

Required body: page_id, recipient_id, message.

Minimal body: {"page_id":"<page-id>","message":{"text":"Hello from Endpointr \ud83d\udc4b"}}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "page_id": "<page-id>",
    "recipient": {
        "id": "<psid>"
    },
    "message": {
        "text": "Hello from Endpointr 👋"
    },
    "messaging_type": "RESPONSE"
}
curl -X POST 'https://api.endpointr.com/v1/messaging/messages' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "page_id": "<page-id>",
    "recipient": {
        "id": "<psid>"
    },
    "message": {
        "text": "Hello from Endpointr 👋"
    },
    "messaging_type": "RESPONSE"
}'
const response = await fetch('https://api.endpointr.com/v1/messaging/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "page_id": "<page-id>",
      "recipient": {
          "id": "<psid>"
      },
      "message": {
          "text": "Hello from Endpointr 👋"
      },
      "messaging_type": "RESPONSE"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messages');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"page_id\": \"<page-id>\",\n    \"recipient\": {\n        \"id\": \"<psid>\"\n    },\n    \"message\": {\n        \"text\": \"Hello from Endpointr 👋\"\n    },\n    \"messaging_type\": \"RESPONSE\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Messenger Profile (menu / ice breakers)

GET/v1/messaging/messenger-profile?page_id=%3Cpage-id%3E&fields=persistent_menu%2Cice_breakers%2Cgreeting%2Cget_started

Read Messenger Profile properties for a Page.

?page_id=<id> required. ?fields= defaults to persistent_menu,get_started,greeting,ice_breakers,whitelisted_domains,account_linking_url.

Required query: page_id.

Minimal query: {"page_id":"<page-id>"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
page_id<page-id>
fieldspersistent_menu,ice_breakers,greeting,get_started
curl -X GET 'https://api.endpointr.com/v1/messaging/messenger-profile?page_id=%3Cpage-id%3E&fields=persistent_menu%2Cice_breakers%2Cgreeting%2Cget_started' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/messenger-profile?page_id=%3Cpage-id%3E&fields=persistent_menu%2Cice_breakers%2Cgreeting%2Cget_started', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messenger-profile?page_id=%3Cpage-id%3E&fields=persistent_menu%2Cice_breakers%2Cgreeting%2Cget_started');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/messaging/messenger-profile

Set Messenger Profile properties. Pass page_id plus any of: persistent_menu, ice_breakers, greeting, get_started, whitelisted_domains. The body (minus page_id) is forwarded to Graph as JSON.

Required body: page_id.

Minimal body: {"page_id":"<page-id>"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "page_id": "<page-id>",
    "get_started": {
        "payload": "GET_STARTED"
    },
    "greeting": [
        {
            "locale": "default",
            "text": "Hi {{user_first_name}}! How can we help?"
        }
    ],
    "ice_breakers": [
        {
            "question": "What are your hours?",
            "payload": "HOURS"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/messaging/messenger-profile' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "page_id": "<page-id>",
    "get_started": {
        "payload": "GET_STARTED"
    },
    "greeting": [
        {
            "locale": "default",
            "text": "Hi {{user_first_name}}! How can we help?"
        }
    ],
    "ice_breakers": [
        {
            "question": "What are your hours?",
            "payload": "HOURS"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/messaging/messenger-profile', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "page_id": "<page-id>",
      "get_started": {
          "payload": "GET_STARTED"
      },
      "greeting": [
          {
              "locale": "default",
              "text": "Hi {{user_first_name}}! How can we help?"
          }
      ],
      "ice_breakers": [
          {
              "question": "What are your hours?",
              "payload": "HOURS"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messenger-profile');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"page_id\": \"<page-id>\",\n    \"get_started\": {\n        \"payload\": \"GET_STARTED\"\n    },\n    \"greeting\": [\n        {\n            \"locale\": \"default\",\n            \"text\": \"Hi {{user_first_name}}! How can we help?\"\n        }\n    ],\n    \"ice_breakers\": [\n        {\n            \"question\": \"What are your hours?\",\n            \"payload\": \"HOURS\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/messaging/messenger-profile/:id

Clear Messenger Profile properties. Body {page_id, fields:["persistent_menu","ice_breakers"]} (or DELETE /v1/messaging/messenger-profile/<field>?page_id=<id> for a single field).

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/messaging/messenger-profile/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/messenger-profile/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/messenger-profile/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Token (introspection)

GET/v1/messaging/token?action=debug-token

Inspect the stored Meta messaging token.

  • No params → debug_token + granted/declined scopes in one call. Best smoke-test after PUT /v1/credentials/meta-messaging.
  • ?action=debug-token → Graph /debug_token.
  • ?action=scopes → /me/permissions granted vs declined.

Required scopes for messaging: pages_messaging, pages_show_list, pages_manage_metadata, pages_read_engagement, plus instagram_basic + instagram_manage_messages for Instagram DMs, and business_management.

Minimal query: {"action":"debug-token"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
actiondebug-token
curl -X GET 'https://api.endpointr.com/v1/messaging/token?action=debug-token' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/token?action=debug-token', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/token?action=debug-token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/messaging/token

Body {action:"extend"} exchanges a short-lived user token for a ~60-day one (fb_exchange_token). Requires app_id/app_secret. Harmless on System User tokens (already long-lived). For production prefer a System User token — it never expires.

Minimal body: {"action":"extend"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "extend"
}
curl -X POST 'https://api.endpointr.com/v1/messaging/token' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "extend"
}'
const response = await fetch('https://api.endpointr.com/v1/messaging/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "extend"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"extend\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

User profile lookup

GET/v1/messaging/profile/:id

Look up a messaging user's public profile by PSID/IGSID.

GET /v1/messaging/profile/<psid>?page_id=<id>[&fields=first_name,last_name,profile_pic,locale]. Requires the Page token of the Page the user is conversing with — pass page_id. The <psid> comes from a conversation participant or an inbound webhook event.

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/profile/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/profile/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/profile/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Webhook subscriptions (per Page)

GET/v1/messaging/webhook-subs

List the per-Page messaging webhook subscriptions (/{page-id}/subscribed_apps) plus the local meta_msg_webhook_subs rows that map (object, object_id=page_id) → customer for inbound routing.

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/messaging/webhook-subs' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/webhook-subs', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/webhook-subs');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/messaging/webhook-subs

Activate messaging webhooks for a Page (POST /{page-id}/subscribed_apps).

{object_id:'<page-id>', object:'page'|'instagram',
 fields:['messages','messaging_postbacks','message_reads','message_reactions','messaging_referrals'],
 callback_url?:'…'}

callback_url defaults to /v1/webhooks/inbound/meta-messaging on the current host.

One-time app setup: the app-level callback URL + verify token (env META_WEBHOOK_VERIFY_TOKEN) for the page/instagram objects is configured once in the Meta App Dashboard. This endpoint is the per-Page activation that routes that Page's events to the app.

Required body: object_id.

Minimal body: {"object_id":"<page-id>"}

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "object_id": "<page-id>",
    "object": "page",
    "fields": [
        "messages",
        "messaging_postbacks",
        "message_reads"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/messaging/webhook-subs' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "object_id": "<page-id>",
    "object": "page",
    "fields": [
        "messages",
        "messaging_postbacks",
        "message_reads"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/messaging/webhook-subs', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "object_id": "<page-id>",
      "object": "page",
      "fields": [
          "messages",
          "messaging_postbacks",
          "message_reads"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/webhook-subs');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"object_id\": \"<page-id>\",\n    \"object\": \"page\",\n    \"fields\": [\n        \"messages\",\n        \"messaging_postbacks\",\n        \"message_reads\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/messaging/webhook-subs/:id

Deactivate messaging webhooks for a Page. :id is the Page id (object_id).

_Requires stored credentials: meta-messaging (PUT /v1/credentials/meta-messaging)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/messaging/webhook-subs/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/messaging/webhook-subs/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/messaging/webhook-subs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Meta Marketing

Ad Sets

GET/v1/marketing/ad-sets/:id

Fetch one ad set.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-sets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-sets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/ad-sets?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false

List ad sets. Filter by account_id (all ad sets in an account), campaign_id (ad sets in one campaign), or both. ?refresh=true forces a live Graph pull.

Minimal query: {"account_id":"{{meta_ad_account_id}}","refresh":"false"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-sets?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-sets?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-sets?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/ad-sets

Create an ad set.

Required: account_id, campaign_id, name, billing_event (e.g. IMPRESSIONS, LINK_CLICKS), optimization_goal (e.g. LINK_CLICKS, REACH, CONVERSIONS, LEAD_GENERATION), targeting (full Meta targeting spec — see https://developers.facebook.com/docs/marketing-api/audiences/reference/targeting-specs).

Budgeting at the ad-set level: daily_budget OR lifetime_budget (cannot have both). Optional: bid_amount, bid_strategy, start_time, end_time, attribution_spec, promoted_object, destination_type.

Sub-verbs like campaigns: set_status, activate, pause, archive.

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "campaign_id": "120000000000000",
    "name": "EU desktop — clicks",
    "status": "PAUSED",
    "billing_event": "IMPRESSIONS",
    "optimization_goal": "LINK_CLICKS",
    "daily_budget": 2500,
    "targeting": {
        "geo_locations": {
            "countries": [
                "DK",
                "SE",
                "NO"
            ]
        },
        "age_min": 25,
        "age_max": 55,
        "publisher_platforms": [
            "facebook",
            "instagram"
        ]
    },
    "start_time": "2026-06-01T00:00:00+0000"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/ad-sets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "campaign_id": "120000000000000",
    "name": "EU desktop — clicks",
    "status": "PAUSED",
    "billing_event": "IMPRESSIONS",
    "optimization_goal": "LINK_CLICKS",
    "daily_budget": 2500,
    "targeting": {
        "geo_locations": {
            "countries": [
                "DK",
                "SE",
                "NO"
            ]
        },
        "age_min": 25,
        "age_max": 55,
        "publisher_platforms": [
            "facebook",
            "instagram"
        ]
    },
    "start_time": "2026-06-01T00:00:00+0000"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-sets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "campaign_id": "120000000000000",
      "name": "EU desktop — clicks",
      "status": "PAUSED",
      "billing_event": "IMPRESSIONS",
      "optimization_goal": "LINK_CLICKS",
      "daily_budget": 2500,
      "targeting": {
          "geo_locations": {
              "countries": [
                  "DK",
                  "SE",
                  "NO"
              ]
          },
          "age_min": 25,
          "age_max": 55,
          "publisher_platforms": [
              "facebook",
              "instagram"
          ]
      },
      "start_time": "2026-06-01T00:00:00+0000"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-sets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"campaign_id\": \"120000000000000\",\n    \"name\": \"EU desktop — clicks\",\n    \"status\": \"PAUSED\",\n    \"billing_event\": \"IMPRESSIONS\",\n    \"optimization_goal\": \"LINK_CLICKS\",\n    \"daily_budget\": 2500,\n    \"targeting\": {\n        \"geo_locations\": {\n            \"countries\": [\n                \"DK\",\n                \"SE\",\n                \"NO\"\n            ]\n        },\n        \"age_min\": 25,\n        \"age_max\": 55,\n        \"publisher_platforms\": [\n            \"facebook\",\n            \"instagram\"\n        ]\n    },\n    \"start_time\": \"2026-06-01T00:00:00+0000\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/marketing/ad-sets/:id

Patch fields on an ad set.

Find ids: the sibling query tool on this resource lists them (each row's resourceName is the id; needs only customer_id).

Find ids: the sibling query tool on this resource lists them (each row's resourceName is the id; needs only customer_id).

Find ids: the sibling query tool on this resource lists them (each row's resourceName is the id; needs only customer_id).

Find ids: the sibling query tool on this resource lists them (each row's resourceName is the id; needs only customer_id).

Find ids: the sibling query tool on this resource lists them (each row's resourceName is the id; needs only customer_id).

Minimal body: {"name":"EU desktop \u2014 clicks (updated)","daily_budget":4000}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "EU desktop — clicks (updated)",
    "daily_budget": 4000
}
curl -X PUT 'https://api.endpointr.com/v1/marketing/ad-sets/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "EU desktop — clicks (updated)",
    "daily_budget": 4000
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-sets/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "EU desktop — clicks (updated)",
      "daily_budget": 4000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"EU desktop — clicks (updated)\",\n    \"daily_budget\": 4000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/ad-sets/:id

Delete an ad set.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/ad-sets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-sets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ad accounts

GET/v1/marketing/ad-accounts

List every ad account the stored token can see.

Discovery walks /me/adaccounts by default. If a business_id is stored in the credential, walks /{business_id}/owned_ad_accounts instead — useful for Business Manager-scoped tokens that own more accounts than they personally have access to.

Every account is mirrored into meta_ad_objects (level=account) so subsequent reads can come from the local store.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/ad-accounts/:id

Fetch one ad account. :id accepts either act_1234567 (Graph-format) or bare 1234567 (the prefix is added automatically).

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ad creatives

GET/v1/marketing/creatives/:id

Fetch one creative.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/creatives/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/creatives/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/creatives/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/creatives?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List creatives under one ad account. ?account_id=act_… required.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/creatives?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/creatives?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/creatives?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/creatives

Create an ad creative. Meta creatives are effectively immutable — there is no PUT.

Minimal link-ad shape: {account_id, name, object_story_spec:{page_id, link_data:{message, link, name, call_to_action:{type:'LEARN_MORE', value:{link}}}}}.

For image-ad: include image_hash (from POST /v1/marketing/ad-images). For video-ad: include video_id (from POST /v1/marketing/ad-videos).

Dry-run: {action:'validate', ...} — runs Meta's dry_run=true validation and returns {valid: bool, errors?} without creating anything.

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Spring landing creative",
    "object_story_spec": {
        "page_id": "0",
        "link_data": {
            "message": "Save 30% this week.",
            "link": "https://example.com/spring",
            "name": "Spring Sale",
            "call_to_action": {
                "type": "SHOP_NOW",
                "value": {
                    "link": "https://example.com/spring"
                }
            }
        }
    }
}
curl -X POST 'https://api.endpointr.com/v1/marketing/creatives' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Spring landing creative",
    "object_story_spec": {
        "page_id": "0",
        "link_data": {
            "message": "Save 30% this week.",
            "link": "https://example.com/spring",
            "name": "Spring Sale",
            "call_to_action": {
                "type": "SHOP_NOW",
                "value": {
                    "link": "https://example.com/spring"
                }
            }
        }
    }
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/creatives', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "name": "Spring landing creative",
      "object_story_spec": {
          "page_id": "0",
          "link_data": {
              "message": "Save 30% this week.",
              "link": "https://example.com/spring",
              "name": "Spring Sale",
              "call_to_action": {
                  "type": "SHOP_NOW",
                  "value": {
                      "link": "https://example.com/spring"
                  }
              }
          }
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/creatives');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"name\": \"Spring landing creative\",\n    \"object_story_spec\": {\n        \"page_id\": \"0\",\n        \"link_data\": {\n            \"message\": \"Save 30% this week.\",\n            \"link\": \"https://example.com/spring\",\n            \"name\": \"Spring Sale\",\n            \"call_to_action\": {\n                \"type\": \"SHOP_NOW\",\n                \"value\": {\n                    \"link\": \"https://example.com/spring\"\n                }\n            }\n        }\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/creatives/:id

Delete a creative.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/creatives/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/creatives/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/creatives/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ad images (upload)

GET/v1/marketing/ad-images?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List uploaded ad images for one account. Optional hashes (JSON array of image hashes) narrows the search.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-images?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-images?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-images?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/ad-images

Upload an image to Meta's ad-account image library. Sync — single multipart POST.

Three ways to supply the bytes (pick one):
- image_base64: raw base64 (no data: URI prefix). Magic bytes are sniffed; PNG/JPEG/GIF/WEBP allowed.
- image_url: any public https URL. Endpointr fetches once, then uploads.
- upload_url: a URL returned by POST /v1/ai/upload (i.e. already on Endpointr's uploads volume).

Response: {id, hash, account_id, asset_id, url, width, height, raw}. The hash is what you attach to creatives. The 1x1 PNG below uploads cleanly — substitute your real bytes.

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "name": "spring-hero.png"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/ad-images' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
    "name": "spring-hero.png"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-images', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "image_base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=",
      "name": "spring-hero.png"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-images');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"image_base64\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=\",\n    \"name\": \"spring-hero.png\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/ad-images/:id

Delete an image by hash. Pass ?account_id=act_… as a query param; the URL id is the image hash.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/ad-images/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-images/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-images/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ad videos (upload, sync + async)

GET/v1/marketing/ad-videos/:id

Status check. :id accepts either an Endpointr asset_id (numeric, returned at upload time) or a Meta video_id.

Response for an asset_id: {asset_id, video_id, upload_state, upload_progress, error?, raw} where upload_state ∈ pending|uploading|processing|ready|failed.

Response for a video_id: the canonical AdVideo shape with the live Graph status.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-videos/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-videos/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-videos/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/ad-videos?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List videos in an ad account.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/ad-videos?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-videos?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-videos?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/ad-videos

Upload an ad video. Picks sync vs async by byte size automatically.

Supply the bytes one of three ways: video_base64, video_url, or upload_url (same semantics as image upload).

  • <50 MB → synchronous single-call upload. Response: {asset_id, video_id, upload_state, mode:'sync', status, raw}. upload_state is usually processing (Meta transcodes for a few seconds; subsequent GET will show ready).
  • ≥50 MB → resumable upload queued as a video_upload job. Response is 202 {asset_id, job_id, upload_state:'pending', mode:'async', bytes}. Poll GET /v1/marketing/ad-videos/<asset_id> to follow upload_progress (0-99) and upload_state. The worker chains a status-poll job after the transfer finishes — final ready lands without further client action.

Optional: title, description, name, mime (defaults to video/mp4).

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "video_url": "https://example.com/marketing/spring-promo.mp4",
    "title": "Spring promo",
    "description": "Hero video — 15s vertical cut"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/ad-videos' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "video_url": "https://example.com/marketing/spring-promo.mp4",
    "title": "Spring promo",
    "description": "Hero video — 15s vertical cut"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-videos', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "video_url": "https://example.com/marketing/spring-promo.mp4",
      "title": "Spring promo",
      "description": "Hero video — 15s vertical cut"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-videos');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"video_url\": \"https://example.com/marketing/spring-promo.mp4\",\n    \"title\": \"Spring promo\",\n    \"description\": \"Hero video — 15s vertical cut\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/ad-videos/:id

Delete a video by its Meta video_id.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/ad-videos/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ad-videos/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ad-videos/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Ads

GET/v1/marketing/ads/:id

Fetch one ad.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/ads/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ads/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/ads?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List ads. Filter by account_id, adset_id, or campaign_id. Use ?action=preview&id=<ad_id>&ad_format=DESKTOP_FEED_STANDARD to fetch HTML previews for any ad format (mobile/desktop/story/reels).

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/ads?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ads?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ads?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/ads

Create an ad.

Required: account_id, name, adset_id, creative_id (or an inline creative spec object).

Optional: status (default Meta-side is ACTIVE; pass PAUSED while staging), tracking_specs.

Sub-verbs: set_status, activate, pause, archive, preview ({action:'preview', id, ad_format}).

Required body: account_id, adset_id, creative_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}","adset_id":"120000000000000","creative_id":"120000000000000"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Carousel — spring",
    "adset_id": "120000000000000",
    "creative_id": "120000000000000",
    "status": "PAUSED"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/ads' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Carousel — spring",
    "adset_id": "120000000000000",
    "creative_id": "120000000000000",
    "status": "PAUSED"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ads', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "name": "Carousel — spring",
      "adset_id": "120000000000000",
      "creative_id": "120000000000000",
      "status": "PAUSED"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ads');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"name\": \"Carousel — spring\",\n    \"adset_id\": \"120000000000000\",\n    \"creative_id\": \"120000000000000\",\n    \"status\": \"PAUSED\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/marketing/ads/:id

Patch an ad.

Minimal body: {"name":"Carousel \u2014 spring (v2)"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Carousel — spring (v2)"
}
curl -X PUT 'https://api.endpointr.com/v1/marketing/ads/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Carousel — spring (v2)"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/ads/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Carousel — spring (v2)"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Carousel — spring (v2)\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/ads/:id

Delete an ad.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/ads/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/ads/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/ads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Audiences (custom + lookalike)

GET/v1/marketing/audiences/:id

Fetch one audience by id (incl. lookalike spec, rule, approximate size).

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/audiences/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/audiences/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/audiences?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List custom + lookalike audiences for an ad account.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/audiences?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/audiences?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/audiences?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/audiences

Create / mutate audiences.

Default action — create custom audience:

{account_id, name, subtype:'CUSTOM', description?,
 customer_file_source:'USER_PROVIDED_ONLY', retention_days?, rule?}

Lookalike (action:'lookalike'): {account_id, name, origin_audience_id, lookalike_spec:{type:'similarity', country:'DK', ratio:0.01}}.

Add users (action:'add_users'): {id, schema:['EMAIL']|['EMAIL','PHONE']|…, users:['jane@a.com', ...]}. Plain-text — SHA-256 hashing happens server-side; already-hashed values are detected and left alone. Phone numbers are stripped to digits before hashing.

Remove users (action:'remove_users'): same shape as add_users.

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Customers who purchased in last 30d",
    "subtype": "CUSTOM",
    "customer_file_source": "USER_PROVIDED_ONLY",
    "retention_days": 180
}
curl -X POST 'https://api.endpointr.com/v1/marketing/audiences' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Customers who purchased in last 30d",
    "subtype": "CUSTOM",
    "customer_file_source": "USER_PROVIDED_ONLY",
    "retention_days": 180
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/audiences', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "name": "Customers who purchased in last 30d",
      "subtype": "CUSTOM",
      "customer_file_source": "USER_PROVIDED_ONLY",
      "retention_days": 180
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/audiences');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"name\": \"Customers who purchased in last 30d\",\n    \"subtype\": \"CUSTOM\",\n    \"customer_file_source\": \"USER_PROVIDED_ONLY\",\n    \"retention_days\": 180\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/audiences/:id

Delete an audience.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/audiences/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/audiences/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Auth (token introspection)

GET/v1/marketing/auth?action=debug-token

Inspect the stored Meta token.

  • No params → both debug_token + granted/declined scopes in one call. Best smoke-test after PUT /v1/credentials/meta-ads.
  • ?action=debug-token → calls Graph's /debug_token: returns scopes, expires_at, issued_at, ad-account/business ids the token can see.
  • ?action=scopes → calls /me/permissions: granted vs declined arrays.

Reading the response

{
  "data": {
    "debug_token": {
      "app_id":     "1234567890123456",
      "type":       "SYSTEM_USER",
      "expires_at": 0,
      "is_valid":   true,
      "scopes":     ["ads_management", "ads_read", "..."]
    },
    "scopes": {
      "granted":  ["ads_management", "ads_read", "..."],
      "declined": []
    }
  }
}
  • expires_at: 0 — System User token (never expires). ✅ Use this for production.
  • expires_at: <future-unix> — short-lived user token. POST {action:"extend"} to swap it for a ~60-day token, OR follow the setup guide to switch to a System User token.
  • Missing scope? Re-issue the token from Meta Business Settings → System Users → *Generate new token* with the scope ticked. The full required set:
  • ads_management, ads_read — every campaign/adset/ad/creative read & write
  • business_management — business-scoped account discovery, catalogs
  • leads_retrieval — lead form data
  • pages_show_list, pages_read_engagement, pages_manage_metadata — pages + leadgen webhooks
  • instagram_basic (optional) — Instagram ads via the connected IG business account

See the Meta Marketing API — first-time setup section in the collection description for the full provisioning walkthrough (creating the app, the Business Portfolio, the System User, and minting the token).

Minimal query: {"action":"debug-token"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
actiondebug-token
curl -X GET 'https://api.endpointr.com/v1/marketing/auth?action=debug-token' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/auth?action=debug-token', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/auth?action=debug-token');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/auth

Body {action: "extend"} exchanges a short-lived user token (1-2 h) for a long-lived (~60 d) one via Meta's fb_exchange_token grant. Requires app_id and app_secret in the stored credential. Harmless on System User tokens (they're already long-lived).

The response carries the new access_token — store it via PUT /v1/credentials/meta-ads to make it the active token.

Recommendation: for production, don't rely on this — generate a System User token from Meta Business Settings instead. System User tokens never expire and don't need periodic refresh. The extend flow is for development scenarios where you only have user-token access (e.g. testing with your own Facebook account during local dev).

Minimal body: {"action":"extend"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "extend"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/auth' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "extend"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/auth', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "extend"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/auth');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"extend\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Campaigns

GET/v1/marketing/campaigns/:id

Fetch one campaign by id. Local mirror first; ?refresh=true forces live.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/campaigns/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/campaigns?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false

List campaigns under one ad account.

?account_id=act_… is required. By default reads from the local mirror; pass ?refresh=true to force a Graph round-trip and re-mirror.

Response: {collection: [Campaign…], source: "local"|"live"}.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
refreshfalse
curl -X GET 'https://api.endpointr.com/v1/marketing/campaigns?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/campaigns?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/campaigns?account_id=%7B%7Bmeta_ad_account_id%7D%7D&refresh=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/campaigns

Create a campaign.

Required: account_id, name, objective (e.g. OUTCOME_TRAFFIC, OUTCOME_SALES, OUTCOME_AWARENESS, OUTCOME_LEADS, OUTCOME_ENGAGEMENT, OUTCOME_APP_PROMOTION). Meta also requires special_ad_categories — pass an empty array [] if none apply (Endpointr fills it in for you when the field is omitted entirely).

Optional: status (PAUSED is the recommended default — created campaigns shouldn't go live unreviewed), daily_budget / lifetime_budget (Meta minor units — cents, øre, etc.), bid_strategy, buying_type, start_time, stop_time.

Sub-verbs via the same POST:
{action:'set_status', id, status:'ACTIVE'|'PAUSED'|'ARCHIVED'|'DELETED'}
{action:'activate', id} — shorthand for status=ACTIVE
{action:'pause', id} — shorthand for status=PAUSED
{action:'archive', id} — shorthand for status=ARCHIVED

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Spring sale — search ads",
    "objective": "OUTCOME_TRAFFIC",
    "status": "PAUSED",
    "special_ad_categories": [],
    "daily_budget": 5000
}
curl -X POST 'https://api.endpointr.com/v1/marketing/campaigns' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "name": "Spring sale — search ads",
    "objective": "OUTCOME_TRAFFIC",
    "status": "PAUSED",
    "special_ad_categories": [],
    "daily_budget": 5000
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/campaigns', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "name": "Spring sale — search ads",
      "objective": "OUTCOME_TRAFFIC",
      "status": "PAUSED",
      "special_ad_categories": [],
      "daily_budget": 5000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"name\": \"Spring sale — search ads\",\n    \"objective\": \"OUTCOME_TRAFFIC\",\n    \"status\": \"PAUSED\",\n    \"special_ad_categories\": [],\n    \"daily_budget\": 5000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/marketing/campaigns/:id

Patch any updatable field on a campaign. Pass only the fields you want to change — Endpointr never echoes back unchanged fields to Graph (avoids accidental resets).

Minimal body: {"name":"Spring sale \u2014 updated","daily_budget":7500}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Spring sale — updated",
    "daily_budget": 7500
}
curl -X PUT 'https://api.endpointr.com/v1/marketing/campaigns/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Spring sale — updated",
    "daily_budget": 7500
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/campaigns/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Spring sale — updated",
      "daily_budget": 7500
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Spring sale — updated\",\n    \"daily_budget\": 7500\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/campaigns/:id

Hard-delete a campaign in Graph and soft-delete (deleted_at) in the local mirror.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/campaigns/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Catalogs (Commerce)

GET/v1/marketing/catalogs/:id

Fetch one catalog.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/catalogs/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/catalogs/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/catalogs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/catalogs

List business-owned product catalogs (requires business_id in stored creds).

Sub-actions:
- ?action=product_sets&id=<catalog_id>
- ?action=products&id=<catalog_id>
- ?action=diagnostics&id=<catalog_id>

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/catalogs' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/catalogs', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/catalogs');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Insights (performance metrics)

GET/v1/marketing/insights?level=campaign&ids=120000000000000&date_preset=last_7d

Pull performance metrics.

Required:
- level ∈ account|campaign|adset|ad
- ids — CSV list, e.g. 120000000000000,120000000000001

Date selection (pick one):
- date_preset — e.g. today, yesterday, last_7d, last_30d, this_month, last_month, this_quarter, maximum
- time_range — JSON {"since":"YYYY-MM-DD","until":"YYYY-MM-DD"}

Optional:
- time_increment — 1 (daily bucket; default for backfills), all_days, 7
- fields — CSV (default: impressions,reach,clicks,spend,cpm,cpc,ctr,actions,action_values,date_start,date_stop)
- breakdowns — CSV (e.g. age,gender, country, publisher_platform,platform_position)
- action_breakdowns — CSV (e.g. action_type)
- cached — true (default) writes results into meta_ad_insights_snapshots. false always live.

Response: {level, ids, date_range, breakdowns, rows:[…], sample_raw:[…], cached}.

Required query: level, ids.

Minimal query: {"level":"campaign","ids":"120000000000000"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
levelcampaign
ids120000000000000
date_presetlast_7d
curl -X GET 'https://api.endpointr.com/v1/marketing/insights?level=campaign&ids=120000000000000&date_preset=last_7d' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/insights?level=campaign&ids=120000000000000&date_preset=last_7d', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/insights?level=campaign&ids=120000000000000&date_preset=last_7d');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/insights

POST equivalent of the GET. Body shape is the same params — use POST when arrays/objects (e.g. breakdowns, time_range) are easier to express in JSON than URL-encoded.

Required body: level, ids.

Minimal body: {"level":"campaign","ids":["120000000000000","120000000000001"]}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "level": "campaign",
    "ids": [
        "120000000000000",
        "120000000000001"
    ],
    "time_range": {
        "since": "2026-05-01",
        "until": "2026-05-21"
    },
    "breakdowns": [
        "age",
        "gender"
    ],
    "cached": true
}
curl -X POST 'https://api.endpointr.com/v1/marketing/insights' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "level": "campaign",
    "ids": [
        "120000000000000",
        "120000000000001"
    ],
    "time_range": {
        "since": "2026-05-01",
        "until": "2026-05-21"
    },
    "breakdowns": [
        "age",
        "gender"
    ],
    "cached": true
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/insights', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "level": "campaign",
      "ids": [
          "120000000000000",
          "120000000000001"
      ],
      "time_range": {
          "since": "2026-05-01",
          "until": "2026-05-21"
      },
      "breakdowns": [
          "age",
          "gender"
      ],
      "cached": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/insights');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"level\": \"campaign\",\n    \"ids\": [\n        \"120000000000000\",\n        \"120000000000001\"\n    ],\n    \"time_range\": {\n        \"since\": \"2026-05-01\",\n        \"until\": \"2026-05-21\"\n    },\n    \"breakdowns\": [\n        \"age\",\n        \"gender\"\n    ],\n    \"cached\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leads + lead forms

GET/v1/marketing/leads/:id

Fetch one lead by Meta lead_id. Also writes/refreshes the local inbox row.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/leads/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/leads/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/leads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/leads?form_id=120000000000000&created_after=2026-05-01

Read lead-gen forms and submissions.

  • ?action=forms&page_id=<page_id> — list lead forms attached to a Page.
  • ?form_id=<form_id>[&created_after=2026-05-01] — pull leads for one form (writes them into the local meta_ad_leads inbox at the same time).
  • ?action=local[&form_id=<form_id>][&since=2026-05-01] — read from the local inbox without touching Graph (the inbox is the destination of the leadgen webhook fan-out).

Minimal query: {"form_id":"120000000000000","created_after":"2026-05-01"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
form_id120000000000000
created_after2026-05-01
curl -X GET 'https://api.endpointr.com/v1/marketing/leads?form_id=120000000000000&created_after=2026-05-01' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/leads?form_id=120000000000000&created_after=2026-05-01', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/leads?form_id=120000000000000&created_after=2026-05-01');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Pages (Facebook)

GET/v1/marketing/pages

List every Facebook Page the token has access to (/me/accounts). Includes the page_access_token for each — that's the value to use when posting to a Page's timeline or attaching the Page to an ad creative.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/pages' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/pages', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/pages');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/pages/:id

Fetch one Page.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/pages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/pages/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/pages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Pixels (datasets + Conversions API)

GET/v1/marketing/pixels/:id

Fetch one pixel.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/pixels/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/pixels/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/pixels/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/marketing/pixels?account_id=%7B%7Bmeta_ad_account_id%7D%7D

List pixels (also called datasets) tied to an ad account.

Required query: account_id.

Minimal query: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
account_id{{meta_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/marketing/pixels?account_id=%7B%7Bmeta_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/pixels?account_id=%7B%7Bmeta_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/pixels?account_id=%7B%7Bmeta_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/pixels

Send server-side conversion events (Conversions API) for one pixel.

Body: {pixel_id, events:[…], test_event_code?}. Each event is the Meta CAPI event shape — event_name, event_time (Unix seconds), action_source, event_source_url, user_data, custom_data.

PII hashing is automatic. All user_data fields in the documented list (em, ph, fn, ln, ge, db, ct, st, zp, country, external_id) are SHA-256 hashed server-side before transmission. Pass plaintext — already-hashed (64-hex) values are detected and left untouched. Pass test_event_code: 'TEST123' to land deliveries in Events Manager's test mode without affecting live metrics.

Required body: pixel_id, events.

Minimal body: {"pixel_id":"1234567890","events":[{"event_name":"Purchase","event_time":"<unix-seconds>","action_source":"website","event_source_url":"https://example.com/checkout/success","user_data":{"em":"jane@example.com","ph":"+4512345678"},"custom_data":{"currency":"DKK","value":499}}]}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "pixel_id": "1234567890",
    "events": [
        {
            "event_name": "Purchase",
            "event_time": "<unix-seconds>",
            "action_source": "website",
            "event_source_url": "https://example.com/checkout/success",
            "user_data": {
                "em": "jane@example.com",
                "ph": "+4512345678"
            },
            "custom_data": {
                "currency": "DKK",
                "value": 499
            }
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/marketing/pixels' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "pixel_id": "1234567890",
    "events": [
        {
            "event_name": "Purchase",
            "event_time": "<unix-seconds>",
            "action_source": "website",
            "event_source_url": "https://example.com/checkout/success",
            "user_data": {
                "em": "jane@example.com",
                "ph": "+4512345678"
            },
            "custom_data": {
                "currency": "DKK",
                "value": 499
            }
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/pixels', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "pixel_id": "1234567890",
      "events": [
          {
              "event_name": "Purchase",
              "event_time": "<unix-seconds>",
              "action_source": "website",
              "event_source_url": "https://example.com/checkout/success",
              "user_data": {
                  "em": "jane@example.com",
                  "ph": "+4512345678"
              },
              "custom_data": {
                  "currency": "DKK",
                  "value": 499
              }
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/pixels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"pixel_id\": \"1234567890\",\n    \"events\": [\n        {\n            \"event_name\": \"Purchase\",\n            \"event_time\": \"<unix-seconds>\",\n            \"action_source\": \"website\",\n            \"event_source_url\": \"https://example.com/checkout/success\",\n            \"user_data\": {\n                \"em\": \"jane@example.com\",\n                \"ph\": \"+4512345678\"\n            },\n            \"custom_data\": {\n                \"currency\": \"DKK\",\n                \"value\": 499\n            }\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sync (refresh local mirror)

POST/v1/marketing/sync

Force a refresh from Graph into the local mirror for one ad account.

mode: 'inline' (default) walks every level (campaign → adset → ad → creative) in this request and returns the row counts. Suitable for accounts under ~5k objects.

mode: 'queue' enqueues a tree_sync job in meta_ad_jobs and returns {queued:true, job_id, account_id}. Use for large accounts so the request returns immediately.

Required body: account_id.

Minimal body: {"account_id":"{{meta_ad_account_id}}"}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "account_id": "{{meta_ad_account_id}}",
    "mode": "inline"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/sync' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "account_id": "{{meta_ad_account_id}}",
    "mode": "inline"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/sync', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "account_id": "{{meta_ad_account_id}}",
      "mode": "inline"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/sync');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"account_id\": \"{{meta_ad_account_id}}\",\n    \"mode\": \"inline\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Webhook subscriptions

GET/v1/marketing/webhook-subscriptions

List app-level webhook subscriptions known to Meta plus the local meta_ad_webhook_subs rows. The local rows map (object, object_id) → customer_id so inbound webhook deliveries are attributed correctly.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/marketing/webhook-subscriptions' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/webhook-subscriptions', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/webhook-subscriptions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/marketing/webhook-subscriptions

Subscribe to a Meta webhook object. Most common shapes:

Leadgen on a Page:

{object:'page', object_id:'<page-id>', fields:['leadgen','feed'],
 callback_url?:'…', verify_token?:'…'}

Ad account changes:
{object:'ad_account', object_id:'act_…', fields:['adsstatus','spend','disable']}

callback_url defaults to the inbound endpoint on the current host (/v1/webhooks/inbound/meta-ads); verify_token defaults to env META_WEBHOOK_VERIFY_TOKEN.

Meta only allows ONE subscription per (app, object), so calling this twice for the same object updates the existing sub.

Required body: object, fields.

Minimal body: {"object":"page","fields":["leadgen"]}

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "object": "page",
    "object_id": "1234567890",
    "fields": [
        "leadgen"
    ],
    "callback_url": "https://api.endpointr.com/v1/webhooks/inbound/meta-ads"
}
curl -X POST 'https://api.endpointr.com/v1/marketing/webhook-subscriptions' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "object": "page",
    "object_id": "1234567890",
    "fields": [
        "leadgen"
    ],
    "callback_url": "https://api.endpointr.com/v1/webhooks/inbound/meta-ads"
}'
const response = await fetch('https://api.endpointr.com/v1/marketing/webhook-subscriptions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "object": "page",
      "object_id": "1234567890",
      "fields": [
          "leadgen"
      ],
      "callback_url": "https://api.endpointr.com/v1/webhooks/inbound/meta-ads"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/webhook-subscriptions');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"object\": \"page\",\n    \"object_id\": \"1234567890\",\n    \"fields\": [\n        \"leadgen\"\n    ],\n    \"callback_url\": \"https://api.endpointr.com/v1/webhooks/inbound/meta-ads\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/marketing/webhook-subscriptions/:id

Unsubscribe from a Meta webhook object. :id here is the object name (e.g. page, ad_account) — Meta keys app-level subs by object, not by row id.

_Requires stored credentials: meta-ads (PUT /v1/credentials/meta-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/marketing/webhook-subscriptions/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/marketing/webhook-subscriptions/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/marketing/webhook-subscriptions/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Misc

Log Stream

GET/v1/misc/log?level=info&from=2026-01-01&to=2026-04-21&limit=100

Query logs by level / date range.

Minimal query: {"level":"info","from":"2026-01-01","to":"2026-04-21","limit":"100"}

AuthorizationBearer YOUR_JWT_TOKEN
levelinfo
from2026-01-01
to2026-04-21
limit100
curl -X GET 'https://api.endpointr.com/v1/misc/log?level=info&from=2026-01-01&to=2026-04-21&limit=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/misc/log?level=info&from=2026-01-01&to=2026-04-21&limit=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/misc/log?level=info&from=2026-01-01&to=2026-04-21&limit=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/misc/log

Append a log entry (requires api_log table).

Required body: message.

Minimal body: {"message":"Something happened"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "level": "info",
    "message": "Something happened",
    "context": {
        "user": "alice"
    }
}
curl -X POST 'https://api.endpointr.com/v1/misc/log' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "level": "info",
    "message": "Something happened",
    "context": {
        "user": "alice"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/misc/log', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "level": "info",
      "message": "Something happened",
      "context": {
          "user": "alice"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/misc/log');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"level\": \"info\",\n    \"message\": \"Something happened\",\n    \"context\": {\n        \"user\": \"alice\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Plagiarism

POST/v1/misc/plagiarism

similar_text comparison with thresholds.

Required body: text1, text2.

Minimal body: {"text1":"Lorem ipsum dolor sit amet.","text2":"Lorem ipsum dolor sit."}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "text1": "Lorem ipsum dolor sit amet.",
    "text2": "Lorem ipsum dolor sit."
}
curl -X POST 'https://api.endpointr.com/v1/misc/plagiarism' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "text1": "Lorem ipsum dolor sit amet.",
    "text2": "Lorem ipsum dolor sit."
}'
const response = await fetch('https://api.endpointr.com/v1/misc/plagiarism', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "text1": "Lorem ipsum dolor sit amet.",
      "text2": "Lorem ipsum dolor sit."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/misc/plagiarism');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"text1\": \"Lorem ipsum dolor sit amet.\",\n    \"text2\": \"Lorem ipsum dolor sit.\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenProvider

OpenProvider — Customers (handles)

GET/v1/openprovider/op-customers

Takes no parameters. Lists customer handles (first page, up to 100). Each result's handle (e.g. XX123456-XX) is the id the customers get/update/delete tools take, and what a domain registration's owner_handle/admin_handle/tech_handle reference. Filter with the customers query tool.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-customers' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-customers/:id

Get one customer by its handle (e.g. XX123456-XX) — the id is the handle string, not a number.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-customers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-customers?pattern=acme

List customer handles. All filters optional. Filters: pattern (matches name/company/handle, wildcard *), email_pattern, company_name_pattern, last_name_pattern, first_name_pattern, handle_pattern, limit, offset.

A customer's handle (e.g. XX123456-XX) is what a domain registration's owner_handle/admin_handle/tech_handle reference.

Minimal query: {"pattern":"acme"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
patternacme
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-customers?pattern=acme' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers?pattern=acme', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers?pattern=acme');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/openprovider/op-customers

Create a customer handle (POST /customers). The response data.handle is the handle you pass as owner_handle when registering a domain.

Required objects: name {first_name, last_name}, address {street, number, zipcode, city, country} (country = ISO-2, e.g. NL), phone {country_code, area_code, subscriber_number}, and email. Add company_name for a company. Some TLDs need extension_additional_data (see OpenProvider's per-TLD requirements).

Minimal:

{"name":{"first_name":"Test","last_name":"Person"},"address":{"street":"Test street","number":"123","zipcode":"1235 XX","city":"Test city","country":"NL"},"phone":{"country_code":"+31","area_code":"111","subscriber_number":"123456"},"email":"test@example.com"}

Required body: name, address, phone, email.

Minimal body: {"name":{"first_name":"Test","last_name":"Person"},"address":{"street":"Test street","number":"123","zipcode":"1235 XX","city":"Test city","country":"NL"},"phone":{"country_code":"+31","area_code":"111","subscriber_number":"123456"},"email":"test@example.com"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": {
        "first_name": "Test",
        "last_name": "Person"
    },
    "address": {
        "street": "Test street",
        "number": "123",
        "zipcode": "1235 XX",
        "city": "Test city",
        "country": "NL"
    },
    "phone": {
        "country_code": "+31",
        "area_code": "111",
        "subscriber_number": "123456"
    },
    "email": "test@example.com"
}
curl -X POST 'https://api.endpointr.com/v1/openprovider/op-customers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": {
        "first_name": "Test",
        "last_name": "Person"
    },
    "address": {
        "street": "Test street",
        "number": "123",
        "zipcode": "1235 XX",
        "city": "Test city",
        "country": "NL"
    },
    "phone": {
        "country_code": "+31",
        "area_code": "111",
        "subscriber_number": "123456"
    },
    "email": "test@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": {
          "first_name": "Test",
          "last_name": "Person"
      },
      "address": {
          "street": "Test street",
          "number": "123",
          "zipcode": "1235 XX",
          "city": "Test city",
          "country": "NL"
      },
      "phone": {
          "country_code": "+31",
          "area_code": "111",
          "subscriber_number": "123456"
      },
      "email": "test@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": {\n        \"first_name\": \"Test\",\n        \"last_name\": \"Person\"\n    },\n    \"address\": {\n        \"street\": \"Test street\",\n        \"number\": \"123\",\n        \"zipcode\": \"1235 XX\",\n        \"city\": \"Test city\",\n        \"country\": \"NL\"\n    },\n    \"phone\": {\n        \"country_code\": \"+31\",\n        \"area_code\": \"111\",\n        \"subscriber_number\": \"123456\"\n    },\n    \"email\": \"test@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/openprovider/op-customers/:id

Update a customer by its handle (PUT). Send only the fields to change (the name fields cannot be changed — clone a handle instead). e.g. new email, address, phone.

Minimal body: {"email":"new@example.com"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "email": "new@example.com"
}
curl -X PUT 'https://api.endpointr.com/v1/openprovider/op-customers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "email": "new@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "email": "new@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"email\": \"new@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/openprovider/op-customers/:id

Delete a customer by its handle. Only succeeds if no active products (domains, certificates) reference it.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/openprovider/op-customers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-customers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenProvider — DNS records

GET/v1/openprovider/op-dns-records?name=example.com&type=A

List a zone's DNS records. name is required (the zone/domain, e.g. example.com). Optional filters: type (A|AAAA|CNAME|MX|TXT|…), record_name_pattern, value_pattern, limit, offset.

Required query: name.

Minimal query: {"name":"example.com"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
nameexample.com
typeA
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-dns-records?name=example.com&type=A' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-dns-records?name=example.com&type=A', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-dns-records?name=example.com&type=A');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/openprovider/op-dns-records

Append one or more records to a zone (maps to the zone update records.add). Required: domain (the zone/domain) and records — a list of {type, name, value, ttl, prio?} (name is the host part, ""/"@" for apex; prio only for MX/SRV). To EDIT or REMOVE records, use /v1/openprovider/op-zones update instead.

Minimal:

{"domain":"example.com","records":[{"type":"A","name":"www","value":"1.2.3.4","ttl":900}]}

Required body: domain, records.

Minimal body: {"domain":"example.com","records":[{"type":"A","name":"www","value":"1.2.3.4","ttl":900}]}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "domain": "example.com",
    "records": [
        {
            "type": "A",
            "name": "www",
            "value": "1.2.3.4",
            "ttl": 900
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/openprovider/op-dns-records' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "domain": "example.com",
    "records": [
        {
            "type": "A",
            "name": "www",
            "value": "1.2.3.4",
            "ttl": 900
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-dns-records', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "domain": "example.com",
      "records": [
          {
              "type": "A",
              "name": "www",
              "value": "1.2.3.4",
              "ttl": 900
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-dns-records');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"domain\": \"example.com\",\n    \"records\": [\n        {\n            \"type\": \"A\",\n            \"name\": \"www\",\n            \"value\": \"1.2.3.4\",\n            \"ttl\": 900\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenProvider — DNS zones

GET/v1/openprovider/op-zones

Takes no parameters. Lists DNS zones (first page, up to 100). Each result's name (the full domain, e.g. example.com) is the id the zones get/update/delete tools take. Filter with the zones query tool.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-zones' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-zones/:id

Get one zone by its name (the full domain, e.g. example.com). Records are included in the response (data.records).

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-zones/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-zones?name_pattern=example.com

List DNS zones. All filters optional. Filters: name_pattern (wildcard *), type (master|slave), with_records (bool), limit, offset.

Minimal query: {"name_pattern":"example.com"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
name_patternexample.com
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-zones?name_pattern=example.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones?name_pattern=example.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones?name_pattern=example.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/openprovider/op-zones

Create a DNS zone. Required: domain {name, extension}. type defaults to master; records is an array of {type, name, value, ttl, prio?} (name is the host part — "" or "@" for the apex; ttl in seconds).

Minimal:

{"domain":{"name":"example","extension":"com"},"type":"master","records":[{"type":"A","name":"www","value":"1.2.3.4","ttl":900},{"type":"A","name":"","value":"1.2.3.4","ttl":900}]}

Required body: domain.

Minimal body: {"domain":{"name":"example","extension":"com"}}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "domain": {
        "name": "example",
        "extension": "com"
    },
    "type": "master",
    "records": [
        {
            "type": "A",
            "name": "www",
            "value": "1.2.3.4",
            "ttl": 900
        },
        {
            "type": "A",
            "name": "",
            "value": "1.2.3.4",
            "ttl": 900
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/openprovider/op-zones' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "domain": {
        "name": "example",
        "extension": "com"
    },
    "type": "master",
    "records": [
        {
            "type": "A",
            "name": "www",
            "value": "1.2.3.4",
            "ttl": 900
        },
        {
            "type": "A",
            "name": "",
            "value": "1.2.3.4",
            "ttl": 900
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "domain": {
          "name": "example",
          "extension": "com"
      },
      "type": "master",
      "records": [
          {
              "type": "A",
              "name": "www",
              "value": "1.2.3.4",
              "ttl": 900
          },
          {
              "type": "A",
              "name": "",
              "value": "1.2.3.4",
              "ttl": 900
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"domain\": {\n        \"name\": \"example\",\n        \"extension\": \"com\"\n    },\n    \"type\": \"master\",\n    \"records\": [\n        {\n            \"type\": \"A\",\n            \"name\": \"www\",\n            \"value\": \"1.2.3.4\",\n            \"ttl\": 900\n        },\n        {\n            \"type\": \"A\",\n            \"name\": \"\",\n            \"value\": \"1.2.3.4\",\n            \"ttl\": 900\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/openprovider/op-zones/:id

Edit a zone by its name (the full domain). This is how you EDIT or REMOVE records — records have no id, so you send them grouped by action under records: add, remove, replace, update (each a list of {type, name, value, ttl, prio?}; update items are {original_record, record}). A record is matched by its full tuple. (To just APPEND records, /v1/openprovider/op-dns-records create is simpler.)

Add a TXT and remove an old A:

{"records":{"add":[{"type":"TXT","name":"","value":"v=spf1 -all","ttl":900}],"remove":[{"type":"A","name":"old","value":"9.9.9.9","ttl":900}]}}

Minimal body: {"records":{"add":[{"type":"TXT","name":"","value":"v=spf1 -all","ttl":900}],"remove":[{"type":"A","name":"old","value":"9.9.9.9","ttl":900}]}}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "records": {
        "add": [
            {
                "type": "TXT",
                "name": "",
                "value": "v=spf1 -all",
                "ttl": 900
            }
        ],
        "remove": [
            {
                "type": "A",
                "name": "old",
                "value": "9.9.9.9",
                "ttl": 900
            }
        ]
    }
}
curl -X PUT 'https://api.endpointr.com/v1/openprovider/op-zones/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "records": {
        "add": [
            {
                "type": "TXT",
                "name": "",
                "value": "v=spf1 -all",
                "ttl": 900
            }
        ],
        "remove": [
            {
                "type": "A",
                "name": "old",
                "value": "9.9.9.9",
                "ttl": 900
            }
        ]
    }
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "records": {
          "add": [
              {
                  "type": "TXT",
                  "name": "",
                  "value": "v=spf1 -all",
                  "ttl": 900
              }
          ],
          "remove": [
              {
                  "type": "A",
                  "name": "old",
                  "value": "9.9.9.9",
                  "ttl": 900
              }
          ]
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"records\": {\n        \"add\": [\n            {\n                \"type\": \"TXT\",\n                \"name\": \"\",\n                \"value\": \"v=spf1 -all\",\n                \"ttl\": 900\n            }\n        ],\n        \"remove\": [\n            {\n                \"type\": \"A\",\n                \"name\": \"old\",\n                \"value\": \"9.9.9.9\",\n                \"ttl\": 900\n            }\n        ]\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/openprovider/op-zones/:id

Delete a zone by its name (the full domain). Irreversible — the zone stops answering queries.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/openprovider/op-zones/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-zones/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-zones/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenProvider — Domain check

POST/v1/openprovider/op-domain-check

Check whether one or more domains are available to register, optionally with the price. This is the discovery step before op-domains create.

Required: domains — an array of {name, extension} objects. Optional with_price (bool) to include the registration price per domain.

Each result carries status (free = available, active = taken) and, with with_price, a price block.

Required body: domains.

Minimal body: {"domains":[{"name":"example","extension":"com"}]}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "domains": [
        {
            "name": "example",
            "extension": "com"
        }
    ],
    "with_price": true
}
curl -X POST 'https://api.endpointr.com/v1/openprovider/op-domain-check' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "domains": [
        {
            "name": "example",
            "extension": "com"
        }
    ],
    "with_price": true
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domain-check', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "domains": [
          {
              "name": "example",
              "extension": "com"
          }
      ],
      "with_price": true
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domain-check');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"domains\": [\n        {\n            \"name\": \"example\",\n            \"extension\": \"com\"\n        }\n    ],\n    \"with_price\": true\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

OpenProvider — Domains

GET/v1/openprovider/op-domains

Start here — takes no parameters. Lists the account's domains (first page, up to 100, with total). Each result's numeric id is what the domains get/update/delete tools and action:'renew' expect. Need filtering or more pages? Use the domains query tool.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-domains' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-domains/:id

Get one domain by its numeric id (the id from a list/create response). Returns full domain details: handles, nameservers, status, expiry/renewal dates.

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-domains/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/openprovider/op-domains?domain_name_pattern=acme%2A&status=ACT

List domains in the account, newest first. All filters optional; call with {} to list all.

Filters: domain_name_pattern (wildcard * allowed, e.g. acme*), extension (TLD without the dot, e.g. com), status (ACT|REQ|PEN|FAI|DEL), contact_handle, ns_group_pattern, limit (default 100, max 1000), offset.

Auth: vault openprovider.

Minimal query: {"domain_name_pattern":"acme*","status":"ACT"}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
domain_name_patternacme*
statusACT
curl -X GET 'https://api.endpointr.com/v1/openprovider/op-domains?domain_name_pattern=acme%2A&status=ACT' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains?domain_name_pattern=acme%2A&status=ACT', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains?domain_name_pattern=acme%2A&status=ACT');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/openprovider/op-domains

Register a domain, or run an action on an existing domain via an action field. Without action, this registers a domain.

Register (no action). Required: domain {name, extension} and owner_handle (a customer handle like XX123456-XX — create one via /v1/openprovider/op-customers). Strongly recommended: period (years, default 1) and name_servers (or ns_group/ns_template_name). admin_handle/tech_handle/billing_handle default to the owner but some TLDs require them explicitly.

Minimal:

{"domain":{"name":"example","extension":"com"},"owner_handle":"XX123456-XX","period":1,"name_servers":[{"name":"ns1.example.com"},{"name":"ns2.example.com"}]}

Renew an existing domain — pass action:'renew' + id (numeric domain id) + period (years):

{"action":"renew","id":123456789,"period":1}

Auth: vault openprovider.

Minimal body: {"domain":{"name":"example","extension":"com"},"owner_handle":"XX123456-XX","period":1,"name_servers":[{"name":"ns1.example.com"},{"name":"ns2.example.com"}]}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "domain": {
        "name": "example",
        "extension": "com"
    },
    "owner_handle": "XX123456-XX",
    "period": 1,
    "name_servers": [
        {
            "name": "ns1.example.com"
        },
        {
            "name": "ns2.example.com"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/openprovider/op-domains' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "domain": {
        "name": "example",
        "extension": "com"
    },
    "owner_handle": "XX123456-XX",
    "period": 1,
    "name_servers": [
        {
            "name": "ns1.example.com"
        },
        {
            "name": "ns2.example.com"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "domain": {
          "name": "example",
          "extension": "com"
      },
      "owner_handle": "XX123456-XX",
      "period": 1,
      "name_servers": [
          {
              "name": "ns1.example.com"
          },
          {
              "name": "ns2.example.com"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"domain\": {\n        \"name\": \"example\",\n        \"extension\": \"com\"\n    },\n    \"owner_handle\": \"XX123456-XX\",\n    \"period\": 1,\n    \"name_servers\": [\n        {\n            \"name\": \"ns1.example.com\"\n        },\n        {\n            \"name\": \"ns2.example.com\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/openprovider/op-domains/:id

Update a domain by its numeric id (PUT). Send only the fields to change — e.g. new contact handles, name_servers/ns_group, autorenew (on|off|default), or is_locked (transfer lock).

Minimal body: {"autorenew":"on","name_servers":[{"name":"ns1.example.com"},{"name":"ns2.example.com"}]}

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "autorenew": "on",
    "name_servers": [
        {
            "name": "ns1.example.com"
        },
        {
            "name": "ns2.example.com"
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/openprovider/op-domains/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "autorenew": "on",
    "name_servers": [
        {
            "name": "ns1.example.com"
        },
        {
            "name": "ns2.example.com"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "autorenew": "on",
      "name_servers": [
          {
              "name": "ns1.example.com"
          },
          {
              "name": "ns2.example.com"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"autorenew\": \"on\",\n    \"name_servers\": [\n        {\n            \"name\": \"ns1.example.com\"\n        },\n        {\n            \"name\": \"ns2.example.com\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/openprovider/op-domains/:id

Delete a domain by its numeric id (DELETE /domains/{id}).

_Requires stored credentials: openprovider (PUT /v1/credentials/openprovider)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/openprovider/op-domains/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/openprovider/op-domains/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/openprovider/op-domains/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Outreach — Encharge

Encharge — Account info

GET/v1/encharge/encharge-account

Takes no parameters. Account info (peopleCount, timezone, activeServices, …) — the smoke test after storing the api_key.

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/encharge/encharge-account' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-account', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-account');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Encharge — Event webhooks

POST/v1/encharge/encharge-webhooks

Subscribe to Encharge events: {eventType, targetUrl}. eventTypes: newUser, updatedUser, unsubscribedUser, added-tag-<TAG>, removed-tag-<TAG> (e.g. added-tag-signed-up). Keep the returned subscription.id — Encharge has no list-subscriptions endpoint; it's the only handle for delete.

Required body: eventType, targetUrl.

Minimal body: {"eventType":"unsubscribedUser","targetUrl":"https://api.example.com/hooks/encharge"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "eventType": "unsubscribedUser",
    "targetUrl": "https://api.example.com/hooks/encharge"
}
curl -X POST 'https://api.endpointr.com/v1/encharge/encharge-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "eventType": "unsubscribedUser",
    "targetUrl": "https://api.example.com/hooks/encharge"
}'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "eventType": "unsubscribedUser",
      "targetUrl": "https://api.example.com/hooks/encharge"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"eventType\": \"unsubscribedUser\",\n    \"targetUrl\": \"https://api.example.com/hooks/encharge\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/encharge/encharge-webhooks/:id

Delete an event subscription by its numeric id (from the create response — no upstream list).

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/encharge/encharge-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-webhooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Encharge — People

GET/v1/encharge/encharge-people?email=alice%40example.com

Fetch specific people by identifier: email (or user_id / id), emails (comma-list), or a raw people array of identifier objects. Encharge has no browse-all endpoint — use the segments tools to page an audience.

Minimal query: {"email":"alice@example.com"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
emailalice@example.com
curl -X GET 'https://api.endpointr.com/v1/encharge/encharge-people?email=alice%40example.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-people?email=alice%40example.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-people?email=alice%40example.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/encharge/encharge-people

UPSERT people (create or update, matched by email/userId/id): one person object or a people array; extra keys become field values (see the fields tool). Fold {action:'unsubscribe', email} to stop all email to a person. Outreach flow: upsert the person, then add a tag (tags tool) — the Flow triggered by that tag sends the email.

Minimal body: {"email":"alice@example.com","firstName":"Alice","companyName":"Example ApS"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "email": "alice@example.com",
    "firstName": "Alice",
    "companyName": "Example ApS"
}
curl -X POST 'https://api.endpointr.com/v1/encharge/encharge-people' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "email": "alice@example.com",
    "firstName": "Alice",
    "companyName": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-people', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "email": "alice@example.com",
      "firstName": "Alice",
      "companyName": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-people');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"email\": \"alice@example.com\",\n    \"firstName\": \"Alice\",\n    \"companyName\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/encharge/encharge-people/:id

Archive a person by id (their email or Encharge id). Add ?force=true for a GDPR-compliant full delete.

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/encharge/encharge-people/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-people/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-people/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Encharge — Person fields

GET/v1/encharge/encharge-fields

Takes no parameters. All person fields; each name is the field id used in people upserts and update/delete here.

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/encharge/encharge-fields' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-fields', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-fields');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/encharge/encharge-fields

Create field(s): {name, type: string|number|boolean|integer|any, title?, format?: date|date-time} or a fields array.

Minimal body: {"name":"plan","title":"Plan","type":"string"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "plan",
    "title": "Plan",
    "type": "string"
}
curl -X POST 'https://api.endpointr.com/v1/encharge/encharge-fields' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "plan",
    "title": "Plan",
    "type": "string"
}'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-fields', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "plan",
      "title": "Plan",
      "type": "string"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-fields');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"plan\",\n    \"title\": \"Plan\",\n    \"type\": \"string\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/encharge/encharge-fields/:id

Modify a field — id is the field name (from the fields list).

Minimal body: {"title":"Subscription plan"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "title": "Subscription plan"
}
curl -X PUT 'https://api.endpointr.com/v1/encharge/encharge-fields/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "title": "Subscription plan"
}'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-fields/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "title": "Subscription plan"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-fields/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"title\": \"Subscription plan\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/encharge/encharge-fields/:id

Delete a field by its name. Removes the property from all people.

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/encharge/encharge-fields/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-fields/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-fields/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Encharge — Segments

GET/v1/encharge/encharge-segments

Takes no parameters. All dynamic segments; each result's numeric id is the segment_id for the people-in-segment query.

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/encharge/encharge-segments' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-segments', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-segments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/encharge/encharge-segments?segment_id=%7B%7Bencharge_segment_id%7D%7D&limit=50

People in one segment. segment_id required (from the segments list). Optional: limit, offset, attributes (field names to return), sort, order (asc|desc).

Required query: segment_id.

Minimal query: {"segment_id":"{{encharge_segment_id}}"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

AuthorizationBearer YOUR_JWT_TOKEN
segment_id{{encharge_segment_id}}
limit50
curl -X GET 'https://api.endpointr.com/v1/encharge/encharge-segments?segment_id=%7B%7Bencharge_segment_id%7D%7D&limit=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-segments?segment_id=%7B%7Bencharge_segment_id%7D%7D&limit=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-segments?segment_id=%7B%7Bencharge_segment_id%7D%7D&limit=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Encharge — Tags (flow triggers)

POST/v1/encharge/encharge-tags

Add tag(s) to a person — the outreach trigger: a Flow triggered by the tag sends the emails. {tag:'newsletter-july', email:'…'}; comma-separate for several tags. Fold {action:'remove', tag, email} to untag. A person's current tags are on their tags field (people query).

Required body: tag.

Minimal body: {"tag":"newsletter-july"}

_Requires stored credentials: encharge (PUT /v1/credentials/encharge)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "tag": "newsletter-july",
    "email": "alice@example.com"
}
curl -X POST 'https://api.endpointr.com/v1/encharge/encharge-tags' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "tag": "newsletter-july",
    "email": "alice@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/encharge/encharge-tags', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "tag": "newsletter-july",
      "email": "alice@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/encharge/encharge-tags');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"tag\": \"newsletter-july\",\n    \"email\": \"alice@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Outreach — SendMails

SendMails — Campaigns (newsletters)

GET/v1/sendmails/sendmails-campaigns

Takes no parameters. Lists all campaigns; each result's uid is the campaign id for get/update/delete/run.

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-campaigns' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-campaigns', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendmails/sendmails-campaigns/:id

One campaign by uid incl. delivery statistics (sent, opens, clicks, bounces).

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendmails/sendmails-campaigns

Create a newsletter DRAFT — or fold {action:'run'|'pause'|'resume', uid} for the send lifecycle. Draft fields: name, list_uid (from the lists tool), subject, from_email, from_name, reply_to, html (the email body), plain?, track_open/track_click ('yes'/'no'), run_at? ('Y-m-d H:i:s' to schedule). New drafts don't send until you run them.

Required body: name, list_uid, subject.

Minimal body: {"name":"July Newsletter","list_uid":"{{sendmails_list_uid}}","subject":"News for July"}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "July Newsletter",
    "list_uid": "{{sendmails_list_uid}}",
    "subject": "News for July",
    "from_email": "news@example.com",
    "from_name": "Example News",
    "reply_to": "news@example.com",
    "html": "<h1>Hello!</h1><p>Our July update…</p>",
    "track_open": "yes",
    "track_click": "yes"
}
curl -X POST 'https://api.endpointr.com/v1/sendmails/sendmails-campaigns' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "July Newsletter",
    "list_uid": "{{sendmails_list_uid}}",
    "subject": "News for July",
    "from_email": "news@example.com",
    "from_name": "Example News",
    "reply_to": "news@example.com",
    "html": "<h1>Hello!</h1><p>Our July update…</p>",
    "track_open": "yes",
    "track_click": "yes"
}'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-campaigns', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "July Newsletter",
      "list_uid": "{{sendmails_list_uid}}",
      "subject": "News for July",
      "from_email": "news@example.com",
      "from_name": "Example News",
      "reply_to": "news@example.com",
      "html": "<h1>Hello!</h1><p>Our July update…</p>",
      "track_open": "yes",
      "track_click": "yes"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"July Newsletter\",\n    \"list_uid\": \"{{sendmails_list_uid}}\",\n    \"subject\": \"News for July\",\n    \"from_email\": \"news@example.com\",\n    \"from_name\": \"Example News\",\n    \"reply_to\": \"news@example.com\",\n    \"html\": \"<h1>Hello!</h1><p>Our July update…</p>\",\n    \"track_open\": \"yes\",\n    \"track_click\": \"yes\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/sendmails/sendmails-campaigns/:id

Update a draft campaign by uid (PATCH upstream). Only drafts ('new' status) are editable.

Minimal body: {"subject":"Updated subject"}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "subject": "Updated subject"
}
curl -X PUT 'https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "subject": "Updated subject"
}'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "subject": "Updated subject"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"subject\": \"Updated subject\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/sendmails/sendmails-campaigns/:id

Delete a campaign by uid.

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

SendMails — Lists

GET/v1/sendmails/sendmails-lists

Takes no parameters — start here. Lists all mail lists; each result's uid is the list_uid the subscribers and campaigns tools need.

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-lists' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-lists', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-lists');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendmails/sendmails-lists/:id

One list by its uid (from the lists list), incl. field definitions.

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-lists/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-lists/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-lists/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendmails/sendmails-lists

Create a mail list — or fold {action:'add_field', uid, type, label, tag} to add a custom subscriber field to an existing list (here tag is the NEW field's uppercase identifier, e.g. COMPANY — distinct from the subscriber tag label field). Create needs name, from_email, from_name and Acelle's contact block (company, address_1, city, zip, country_id, email).

Minimal body: {"name":"Newsletter","from_email":"news@example.com","from_name":"Example News","default_subject":"News from Example","contact":{"company":"Example ApS","address_1":"Street 1","city":"Copenhagen","zip":"2100","country_id":"dk","email":"news@example.com"}}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Newsletter",
    "from_email": "news@example.com",
    "from_name": "Example News",
    "default_subject": "News from Example",
    "contact": {
        "company": "Example ApS",
        "address_1": "Street 1",
        "city": "Copenhagen",
        "zip": "2100",
        "country_id": "dk",
        "email": "news@example.com"
    }
}
curl -X POST 'https://api.endpointr.com/v1/sendmails/sendmails-lists' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Newsletter",
    "from_email": "news@example.com",
    "from_name": "Example News",
    "default_subject": "News from Example",
    "contact": {
        "company": "Example ApS",
        "address_1": "Street 1",
        "city": "Copenhagen",
        "zip": "2100",
        "country_id": "dk",
        "email": "news@example.com"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-lists', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Newsletter",
      "from_email": "news@example.com",
      "from_name": "Example News",
      "default_subject": "News from Example",
      "contact": {
          "company": "Example ApS",
          "address_1": "Street 1",
          "city": "Copenhagen",
          "zip": "2100",
          "country_id": "dk",
          "email": "news@example.com"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-lists');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Newsletter\",\n    \"from_email\": \"news@example.com\",\n    \"from_name\": \"Example News\",\n    \"default_subject\": \"News from Example\",\n    \"contact\": {\n        \"company\": \"Example ApS\",\n        \"address_1\": \"Street 1\",\n        \"city\": \"Copenhagen\",\n        \"zip\": \"2100\",\n        \"country_id\": \"dk\",\n        \"email\": \"news@example.com\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/sendmails/sendmails-lists/:id

Delete a list by its uid. Irreversible — subscribers on it are removed.

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/sendmails/sendmails-lists/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-lists/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-lists/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

SendMails — Subscribers

GET/v1/sendmails/sendmails-subscribers/:id

One subscriber by their uid (from the subscribers query).

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendmails/sendmails-subscribers?list_uid=%7B%7Bsendmails_list_uid%7D%7D&per_page=50

List subscribers on a list. list_uid is required — get it from the SendMails lists tool. Optional: per_page, page.

Required query: list_uid.

Minimal query: {"list_uid":"{{sendmails_list_uid}}"}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
list_uid{{sendmails_list_uid}}
per_page50
curl -X GET 'https://api.endpointr.com/v1/sendmails/sendmails-subscribers?list_uid=%7B%7Bsendmails_list_uid%7D%7D&per_page=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-subscribers?list_uid=%7B%7Bsendmails_list_uid%7D%7D&per_page=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-subscribers?list_uid=%7B%7Bsendmails_list_uid%7D%7D&per_page=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendmails/sendmails-subscribers

Add a subscriber. Fields are keyed by their Acelle identifier (the uppercase field key, e.g. EMAIL, FIRST_NAME, LAST_NAME, plus any custom fields). Labels go in the built-in tag field — SINGULAR, a comma-separated string like "vip,newsletter" (there is no tags array). Required: list_uid (from the lists tool) + EMAIL. Or fold {action:'subscribe'|'unsubscribe', uid} to flip an existing subscriber's status.

Required body: list_uid, EMAIL.

Minimal body: {"list_uid":"{{sendmails_list_uid}}","EMAIL":"alice@example.com"}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "list_uid": "{{sendmails_list_uid}}",
    "EMAIL": "alice@example.com",
    "FIRST_NAME": "Alice",
    "tag": "vip,newsletter"
}
curl -X POST 'https://api.endpointr.com/v1/sendmails/sendmails-subscribers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "list_uid": "{{sendmails_list_uid}}",
    "EMAIL": "alice@example.com",
    "FIRST_NAME": "Alice",
    "tag": "vip,newsletter"
}'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-subscribers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "list_uid": "{{sendmails_list_uid}}",
      "EMAIL": "alice@example.com",
      "FIRST_NAME": "Alice",
      "tag": "vip,newsletter"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-subscribers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"list_uid\": \"{{sendmails_list_uid}}\",\n    \"EMAIL\": \"alice@example.com\",\n    \"FIRST_NAME\": \"Alice\",\n    \"tag\": \"vip,newsletter\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/sendmails/sendmails-subscribers/:id

Update a subscriber's fields by uid (PATCH — writable). Send only the fields you want to change, keyed by their Acelle identifier (EMAIL, FIRST_NAME, …). Set labels via the tag field (SINGULAR, comma-separated string — NOT tags, not an array); a tag you send REPLACES the subscriber's tag string. Deliverability is the status field (subscribed/unsubscribed).

Minimal body: {"FIRST_NAME":"Alice (updated)","tag":"vip,newsletter","status":"subscribed"}

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "FIRST_NAME": "Alice (updated)",
    "tag": "vip,newsletter",
    "status": "subscribed"
}
curl -X PUT 'https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "FIRST_NAME": "Alice (updated)",
    "tag": "vip,newsletter",
    "status": "subscribed"
}'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "FIRST_NAME": "Alice (updated)",
      "tag": "vip,newsletter",
      "status": "subscribed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"FIRST_NAME\": \"Alice (updated)\",\n    \"tag\": \"vip,newsletter\",\n    \"status\": \"subscribed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/sendmails/sendmails-subscribers/:id

Delete a subscriber by uid (removes them from the list entirely; prefer {action:'unsubscribe'} to keep the record).

_Requires stored credentials: sendmails (PUT /v1/credentials/sendmails)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendmails/sendmails-subscribers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Outreach — Sendr

Sendr — Analyze website (personas)

POST/v1/sendr/sendr-analyze-website

Analyze a website and generate 2-5 buyer personas (name, tagline, bio, goals, pain points) + an about text. Required: url. Useful input for campaign copy and targeting.

Required body: url.

Minimal body: {"url":"https://example.com"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com"
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-analyze-website' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-analyze-website', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-analyze-website');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Campaigns (sequences, read-only)

GET/v1/sendr/sendr-campaigns

Takes no parameters. Lists outreach campaigns with status (DRAFT|ACTIVE|PAUSED), stepsCount, contactsCount, and sheetId. Campaigns are read-only via the API (built in the Sendr app) — add contacts via the sheet-rows tool using the campaign's sheetId.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-campaigns' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-campaigns', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-campaigns/:id

One campaign by its numeric id (from the campaigns list).

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-campaigns/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-campaigns?status=ACTIVE

Filtered campaign list. Optional: search, status (DRAFT|ACTIVE|PAUSED), page, limit (max 100), sort, order.

Minimal query: {"status":"ACTIVE"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
statusACTIVE
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-campaigns?status=ACTIVE' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-campaigns?status=ACTIVE', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-campaigns?status=ACTIVE');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Dynamic audio

POST/v1/sendr/sendr-dynamic-audio

Queue personalized-audio generation: targetWord in the source audio is replaced with replacementWord (voice-cloned). Required: audioUrl (public), targetWord, replacementWord. Optional: elevenlabsId, languageCode, webhookUrl. Returns {jobId}.

Required body: audioUrl, targetWord, replacementWord.

Minimal body: {"audioUrl":"https://example.com/audio.mp3","targetWord":"NAME","replacementWord":"Alice"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "audioUrl": "https://example.com/audio.mp3",
    "targetWord": "NAME",
    "replacementWord": "Alice"
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-dynamic-audio' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "audioUrl": "https://example.com/audio.mp3",
    "targetWord": "NAME",
    "replacementWord": "Alice"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-dynamic-audio', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "audioUrl": "https://example.com/audio.mp3",
      "targetWord": "NAME",
      "replacementWord": "Alice"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-dynamic-audio');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"audioUrl\": \"https://example.com/audio.mp3\",\n    \"targetWord\": \"NAME\",\n    \"replacementWord\": \"Alice\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Inbox (reply threads)

GET/v1/sendr/sendr-inbox

Takes no parameters. The 25 latest reply threads across channels; each result's id is the thread_id for messages/actions.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-inbox/:id

One thread by its thread_id (from the list/query).

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-inbox?status=unread&limit=25

Filtered threads. Optional: search (min 3 chars), status (unread|deleted), channels, campaign_ids (from the inbox-campaigns tool), tag_ids (from the inbox-tags tool), assigned_to_seat_ids, unassigned, starred, cursor, limit (max 100).

Minimal query: {"status":"unread","limit":25}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
statusunread
limit25
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox?status=unread&limit=25' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox?status=unread&limit=25', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox?status=unread&limit=25');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendr/sendr-inbox

Act on threads via action: send {thread_id, body} = reply in-thread; read/unread, star/unstar {thread_id}; tag/untag {thread_id, tag_id}; delete/restore {thread_ids:[…]} (bulk, max 100); assign {thread_ids:[…], seat_id} (null unassigns; seat ids via the seat tool).

Minimal body: {"action":"send","thread_id":"{{sendr_thread_id}}","body":"Thanks \u2014 following up with the details now."}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "send",
    "thread_id": "{{sendr_thread_id}}",
    "body": "Thanks — following up with the details now."
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-inbox' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "send",
    "thread_id": "{{sendr_thread_id}}",
    "body": "Thanks — following up with the details now."
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "send",
      "thread_id": "{{sendr_thread_id}}",
      "body": "Thanks — following up with the details now."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"send\",\n    \"thread_id\": \"{{sendr_thread_id}}\",\n    \"body\": \"Thanks — following up with the details now.\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Inbox campaigns

GET/v1/sendr/sendr-inbox-campaigns

Takes no parameters. The campaigns that have inbox threads — the id source for the inbox campaign_ids filter.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox-campaigns' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-campaigns', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Inbox messages + attachments

GET/v1/sendr/sendr-inbox-messages?thread_id=%7B%7Bsendr_thread_id%7D%7D&limit=50

A thread's messages. thread_id required (from the inbox list/query); optional cursor, limit. Add message_id for that message's attachment list; add attachment_id too to download it (returned as {base64, mime_type, bytes}). Reply via the inbox tool's 'send' action.

Required query: thread_id.

Minimal query: {"thread_id":"{{sendr_thread_id}}"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
thread_id{{sendr_thread_id}}
limit50
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox-messages?thread_id=%7B%7Bsendr_thread_id%7D%7D&limit=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-messages?thread_id=%7B%7Bsendr_thread_id%7D%7D&limit=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-messages?thread_id=%7B%7Bsendr_thread_id%7D%7D&limit=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Inbox tags

GET/v1/sendr/sendr-inbox-tags

Takes no parameters. Workspace inbox tags — each numeric id is the tag_id for the inbox tag/untag actions and the tag_ids filter.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox-tags' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-tags', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-tags');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-inbox-tags?search=hot

Tags filtered by search.

Minimal query: {"search":"hot"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
searchhot
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-inbox-tags?search=hot' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-tags?search=hot', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-tags?search=hot');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendr/sendr-inbox-tags

Create a tag: name (required, max 50) + optional color.

Required body: name.

Minimal body: {"name":"hot-lead"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "hot-lead",
    "color": "#f43f5e"
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-inbox-tags' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "hot-lead",
    "color": "#f43f5e"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-tags', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "hot-lead",
      "color": "#f43f5e"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-tags');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"hot-lead\",\n    \"color\": \"#f43f5e\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/sendr/sendr-inbox-tags/:id

Edit a tag's name/color by its numeric id.

Minimal body: {"name":"warm-lead"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "warm-lead"
}
curl -X PUT 'https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "warm-lead"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "warm-lead"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"warm-lead\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/sendr/sendr-inbox-tags/:id

Delete a tag by its numeric id (cascade-removes it from all threads).

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-inbox-tags/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Page templates

GET/v1/sendr/sendr-page-templates

Takes no parameters. Lists your page templates — each id is the templateId the pages tool needs.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-page-templates' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-page-templates', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-page-templates');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-page-templates/:id

A template's variable tags by template id. Use the tags as keys in the pages tool's variablesValues.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-page-templates/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-page-templates/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-page-templates/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Personalized pages

GET/v1/sendr/sendr-pages/:id

A generated page's current state by pageId or slug (from the create response): eventStatus (pending|done|failed), generated assets (gifUrl, audioUrl, lipsyncVideo, backgroundScreenshot), processing flags, errorMessage.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-pages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-pages/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-pages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendr/sendr-pages

Generate a personalized page from a template. Required: templateId (from the page-templates tool). Optional: variablesValues (keys = the template's variable tags), gifSource (landing-page|video-thumbnail|dynamic-website|linkedin-profile), gifWebsiteUrl (required when gifSource=dynamic-website), videoBackgroundUrl, videoBackgroundType (static|cursor|scroll), gifHyperlinkText, attributes (echoed in webhooks — e.g. {sheet, row}), webhookUrl (inline, THIS page only — use workspace webhooks for a standing subscription). Returns {pageId, pageUrl, pagePreviewUrl, warnings} — poll the get verb with pageId until eventStatus is done|failed.

Required body: templateId.

Minimal body: {"templateId":123}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "templateId": 123,
    "variablesValues": {
        "first_name": "Alice",
        "company": "Example ApS"
    }
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-pages' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "templateId": 123,
    "variablesValues": {
        "first_name": "Alice",
        "company": "Example ApS"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-pages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "templateId": 123,
      "variablesValues": {
          "first_name": "Alice",
          "company": "Example ApS"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-pages');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"templateId\": 123,\n    \"variablesValues\": {\n        \"first_name\": \"Alice\",\n        \"company\": \"Example ApS\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Seat (API user)

GET/v1/sendr/sendr-seat

Takes no parameters. The API user's workspace/seat info — the smoke test after storing the api_key, and the seat-id source for inbox assignment.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-seat' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-seat', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-seat');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Sheet columns (field schema)

GET/v1/sendr/sendr-sheet-columns?sheet_id=%7B%7Bsendr_sheet_id%7D%7D

A sheet's columns. sheet_id required (from the sheets list). Read this before adding rows — column names are the keys a row accepts; enrichment columns report run status.

Required query: sheet_id.

Minimal query: {"sheet_id":"{{sendr_sheet_id}}"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
sheet_id{{sendr_sheet_id}}
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-sheet-columns?sheet_id=%7B%7Bsendr_sheet_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-sheet-columns?sheet_id=%7B%7Bsendr_sheet_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-sheet-columns?sheet_id=%7B%7Bsendr_sheet_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Sheet rows (add contacts)

POST/v1/sendr/sendr-sheet-rows

How you add a contact to a campaign: add a row to the campaign's sheet. sheet_id required (from the sheets list / a campaign's sheetId); every other key is a cell keyed by column name (see the sheet-columns tool). Returns {rowId, rowNumber}.

Required body: sheet_id.

Minimal body: {"sheet_id":"{{sendr_sheet_id}}"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "sheet_id": "{{sendr_sheet_id}}",
    "Email": "alice@example.com",
    "First Name": "Alice",
    "Company": "Example ApS"
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-sheet-rows' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "sheet_id": "{{sendr_sheet_id}}",
    "Email": "alice@example.com",
    "First Name": "Alice",
    "Company": "Example ApS"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-sheet-rows', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "sheet_id": "{{sendr_sheet_id}}",
      "Email": "alice@example.com",
      "First Name": "Alice",
      "Company": "Example ApS"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-sheet-rows');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"sheet_id\": \"{{sendr_sheet_id}}\",\n    \"Email\": \"alice@example.com\",\n    \"First Name\": \"Alice\",\n    \"Company\": \"Example ApS\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Sheets (contact tables)

GET/v1/sendr/sendr-sheets

Takes no parameters. Lists contact sheets; each result's id is the sheet_id the columns and rows tools need, and campaignId links it to a campaign.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-sheets' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-sheets', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-sheets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-sheets/:id

One sheet by its id (from the sheets list).

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-sheets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-sheets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-sheets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/sendr/sendr-sheets?name=Leads&limit=50

Filtered sheet list. Optional: name, campaignId, offset, limit (20-1000), user, date.

Minimal query: {"name":"Leads","limit":50}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
nameLeads
limit50
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-sheets?name=Leads&limit=50' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-sheets?name=Leads&limit=50', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-sheets?name=Leads&limit=50');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Video generation

POST/v1/sendr/sendr-video

Queue personalized-video generation (dynamic audio merged into / lip-synced onto a source video). Required: audioUrl, targetWord, replacementWord, videoUrl (all public URLs). Optional: mode (merge|lipsync|video_only, default merge), elevenlabsId, languageCode, pageSlug, webhookUrl. Returns {jobId}.

Required body: audioUrl, targetWord, replacementWord, videoUrl.

Minimal body: {"audioUrl":"https://example.com/audio.mp3","targetWord":"NAME","replacementWord":"Alice","videoUrl":"https://example.com/video.mp4"}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "audioUrl": "https://example.com/audio.mp3",
    "targetWord": "NAME",
    "replacementWord": "Alice",
    "videoUrl": "https://example.com/video.mp4",
    "mode": "lipsync"
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-video' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "audioUrl": "https://example.com/audio.mp3",
    "targetWord": "NAME",
    "replacementWord": "Alice",
    "videoUrl": "https://example.com/video.mp4",
    "mode": "lipsync"
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-video', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "audioUrl": "https://example.com/audio.mp3",
      "targetWord": "NAME",
      "replacementWord": "Alice",
      "videoUrl": "https://example.com/video.mp4",
      "mode": "lipsync"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-video');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"audioUrl\": \"https://example.com/audio.mp3\",\n    \"targetWord\": \"NAME\",\n    \"replacementWord\": \"Alice\",\n    \"videoUrl\": \"https://example.com/video.mp4\",\n    \"mode\": \"lipsync\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Sendr — Workspace webhooks

GET/v1/sendr/sendr-webhooks

Takes no parameters. Lists workspace webhooks (page render + engagement events). Sendr keys webhooks by their url — that's the id the folded actions take.

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/sendr/sendr-webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/sendr/sendr-webhooks

Create a webhook ({name, url, events?, attributes?}) — or a folded action keyed by url: {action:'update', url, …} (PATCH), {action:'delete', url}, {action:'toggle', url, enabled}, {action:'reveal_secret', url}. Event types: page:pending/done/failed, engagement:page_view/audio_play/video_play/button_click/video_emoji_click/video_comment/meeting_booked.

Minimal body: {"name":"Page events","url":"https://api.example.com/hooks/sendr","events":["page:done","engagement:page_view"]}

_Requires stored credentials: sendr (PUT /v1/credentials/sendr)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Page events",
    "url": "https://api.example.com/hooks/sendr",
    "events": [
        "page:done",
        "engagement:page_view"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/sendr/sendr-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Page events",
    "url": "https://api.example.com/hooks/sendr",
    "events": [
        "page:done",
        "engagement:page_view"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/sendr/sendr-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Page events",
      "url": "https://api.example.com/hooks/sendr",
      "events": [
          "page:done",
          "engagement:page_view"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/sendr/sendr-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Page events\",\n    \"url\": \"https://api.example.com/hooks/sendr\",\n    \"events\": [\n        \"page:done\",\n        \"engagement:page_view\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads

Reddit Ads — Ad Account History

POST/v1/redditads/reddit-history

Change history for an ad account (who changed what, when) — a query-by-POST endpoint upstream, so it lives on create even though it only reads. Required: ad_account_id. Remaining fields are Reddit's filter body (entity types, date range), forwarded verbatim.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-history' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-history', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-history');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Ad Accounts

GET/v1/redditads/reddit-ad-accounts/:id

One ad account by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-ad-accounts?business_id=%7B%7Breddit_business_id%7D%7D

List ad accounts under a business. Required: business_id (from the businesses list). Each result's id is the ad_account_id the campaign/ad-group/ad/audience/report tools need. Paginate with page.size/page.token or a returned next_url.

Required query: business_id.

Minimal query: {"business_id":"{{reddit_business_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
business_id{{reddit_business_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ad-accounts?business_id=%7B%7Breddit_business_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-accounts?business_id=%7B%7Breddit_business_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-accounts?business_id=%7B%7Breddit_business_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-ad-accounts

This is a SEARCH, not creation — Reddit's API cannot create ad accounts. Maps to POST /businesses/{business_id}/ad_accounts/query: a filtered ad-account search. Required: business_id; remaining fields are Reddit's filter body, forwarded verbatim.

Required body: business_id.

Minimal body: {"business_id":"{{reddit_business_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "business_id": "{{reddit_business_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-ad-accounts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "business_id": "{{reddit_business_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-accounts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "business_id": "{{reddit_business_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"business_id\": \"{{reddit_business_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-ad-accounts/:id

Update ad-account fields (name, time_zone_id, …). PATCH upstream.

Minimal body: {"name":"Acme \u2014 Performance"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Acme — Performance"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Acme — Performance"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Acme — Performance"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-accounts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Acme — Performance\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Ad Groups

GET/v1/redditads/reddit-ad-groups/:id

One ad group by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-ad-groups?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List ad groups under an ad account — the id source for ad-group get/update and ad_group_id on ad create. Required: ad_account_id.

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ad-groups?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-groups?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-groups?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-ad-groups

Create an ad group (targeting, bid, schedule, budget live here). Required: ad_account_id; flat fields auto-wrapped as {data:{…}}. Reference the parent campaign_id; bids/budgets in microcurrency; validate targeting values with the reddit-targeting tools first.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "campaign_id": "{{reddit_campaign_id}}",
    "name": "US — broad",
    "configured_status": "PAUSED",
    "bid_type": "CPC",
    "bid_value": 1500000,
    "goal_type": "DAILY_SPEND",
    "goal_value": 10000000
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-ad-groups' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "campaign_id": "{{reddit_campaign_id}}",
    "name": "US — broad",
    "configured_status": "PAUSED",
    "bid_type": "CPC",
    "bid_value": 1500000,
    "goal_type": "DAILY_SPEND",
    "goal_value": 10000000
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-groups', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "campaign_id": "{{reddit_campaign_id}}",
      "name": "US — broad",
      "configured_status": "PAUSED",
      "bid_type": "CPC",
      "bid_value": 1500000,
      "goal_type": "DAILY_SPEND",
      "goal_value": 10000000
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-groups');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"campaign_id\": \"{{reddit_campaign_id}}\",\n    \"name\": \"US — broad\",\n    \"configured_status\": \"PAUSED\",\n    \"bid_type\": \"CPC\",\n    \"bid_value\": 1500000,\n    \"goal_type\": \"DAILY_SPEND\",\n    \"goal_value\": 10000000\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-ad-groups/:id

Update an ad group (PATCH upstream). Pause/resume/archive via configured_status — no delete.

Minimal body: {"configured_status":"PAUSED"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "configured_status": "PAUSED"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "configured_status": "PAUSED"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "configured_status": "PAUSED"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ad-groups/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"configured_status\": \"PAUSED\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Ads

GET/v1/redditads/reddit-ads/:id

One ad by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ads/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ads/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-ads?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List ads under an ad account. Required: ad_account_id.

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-ads?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ads?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ads?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-ads

Create an ad — binds a post to an ad group. Required: ad_account_id; reference ad_group_id + post_id (create the post first via reddit-posts). Flat fields auto-wrapped as {data:{…}}.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "ad_group_id": "{{reddit_ad_group_id}}",
    "post_id": "{{reddit_post_id}}",
    "configured_status": "PAUSED"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-ads' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "ad_group_id": "{{reddit_ad_group_id}}",
    "post_id": "{{reddit_post_id}}",
    "configured_status": "PAUSED"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ads', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "ad_group_id": "{{reddit_ad_group_id}}",
      "post_id": "{{reddit_post_id}}",
      "configured_status": "PAUSED"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ads');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"ad_group_id\": \"{{reddit_ad_group_id}}\",\n    \"post_id\": \"{{reddit_post_id}}\",\n    \"configured_status\": \"PAUSED\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-ads/:id

Update an ad (PATCH upstream). Pause/resume/archive via configured_status — no delete.

Minimal body: {"configured_status":"PAUSED"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "configured_status": "PAUSED"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-ads/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "configured_status": "PAUSED"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-ads/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "configured_status": "PAUSED"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-ads/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"configured_status\": \"PAUSED\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Apps + SKAdNetwork

GET/v1/redditads/reddit-apps?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

Mobile apps (for app-install campaigns). Required — exactly one of: ad_account_id (list the account's apps) | app_id (+ optional report: last_fired_at (default, MMP signal freshness) | skan_availability (SKAdNetwork)).

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-apps?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-apps?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-apps?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Businesses

GET/v1/redditads/reddit-businesses

Takes no parameters — start here. Lists the businesses the member belongs to; each result's id is the business_id the ad-accounts, pixels, profiles and product-catalogs tools need.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-businesses' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-businesses', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-businesses');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-businesses/:id

One business by id (from the businesses list).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-businesses/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-businesses/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-businesses/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-businesses/:id

Update business fields (name, …). PATCH upstream — send only the fields to change.

Minimal body: {"name":"Acme Media"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Acme Media"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-businesses/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Acme Media"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-businesses/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Acme Media"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-businesses/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Acme Media\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Campaigns

GET/v1/redditads/reddit-campaigns/:id

One campaign by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-campaigns/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-campaigns/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-campaigns?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List campaigns under an ad account — the id source for campaign get/update and campaign_id on ad-group create. Required: ad_account_id (from reddit-ad-accounts).

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-campaigns?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-campaigns?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-campaigns?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-campaigns

Create a campaign. Required: ad_account_id; flat campaign fields are auto-wrapped as Reddit's {data:{…}} envelope. objective: e.g. TRAFFIC | CONVERSIONS | AWARENESS_AND_REACH | APP_INSTALLS | CATALOG_SALES | LEAD_GENERATION | VIDEO_VIEWS. Start configured_status: PAUSED to review before spending; money fields are in microcurrency (5000000 = 5.00).

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Spring sale — traffic",
    "objective": "TRAFFIC",
    "configured_status": "PAUSED",
    "funding_instrument_id": "{{reddit_funding_instrument_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-campaigns' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Spring sale — traffic",
    "objective": "TRAFFIC",
    "configured_status": "PAUSED",
    "funding_instrument_id": "{{reddit_funding_instrument_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-campaigns', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "name": "Spring sale — traffic",
      "objective": "TRAFFIC",
      "configured_status": "PAUSED",
      "funding_instrument_id": "{{reddit_funding_instrument_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-campaigns');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"name\": \"Spring sale — traffic\",\n    \"objective\": \"TRAFFIC\",\n    \"configured_status\": \"PAUSED\",\n    \"funding_instrument_id\": \"{{reddit_funding_instrument_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-campaigns/:id

Update a campaign (PATCH upstream — send only the fields to change). Pause with {configured_status:"PAUSED"}, resume with ACTIVE, archive with ARCHIVED — there is no delete.

Minimal body: {"configured_status":"PAUSED"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "configured_status": "PAUSED"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-campaigns/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "configured_status": "PAUSED"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-campaigns/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "configured_status": "PAUSED"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-campaigns/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"configured_status\": \"PAUSED\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Catalog Imports

GET/v1/redditads/reddit-catalog-imports?catalog_id=%7B%7Breddit_catalog_id%7D%7D

Catalog import runs + diagnostics. Required — exactly one of: catalog_id (list the catalog's import runs) | import_id (+ optional view: issues (default) | report).

Minimal query: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
catalog_id{{reddit_catalog_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-catalog-imports?catalog_id=%7B%7Breddit_catalog_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-catalog-imports?catalog_id=%7B%7Breddit_catalog_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-catalog-imports?catalog_id=%7B%7Breddit_catalog_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Creative Assets

GET/v1/redditads/reddit-creative-assets/:id

One creative asset by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-creative-assets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-creative-assets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-creative-assets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-creative-assets?profile_id=%7B%7Breddit_profile_id%7D%7D

List a profile's uploaded creative media (images/video). Required: profile_id.

Required query: profile_id.

Minimal query: {"profile_id":"{{reddit_profile_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
profile_id{{reddit_profile_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-creative-assets?profile_id=%7B%7Breddit_profile_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-creative-assets?profile_id=%7B%7Breddit_profile_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-creative-assets?profile_id=%7B%7Breddit_profile_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Custom Audiences

GET/v1/redditads/reddit-custom-audiences/:id

One custom audience by id (size, status).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-custom-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List an ad account's custom (customer-list) audiences. Required: ad_account_id.

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-custom-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-custom-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-custom-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-custom-audiences

Create an empty custom audience, then fill it via update. Required: ad_account_id.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Newsletter subscribers",
    "type": "CUSTOMER_LIST"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-custom-audiences' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Newsletter subscribers",
    "type": "CUSTOMER_LIST"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-custom-audiences', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "name": "Newsletter subscribers",
      "type": "CUSTOMER_LIST"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-custom-audiences');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"name\": \"Newsletter subscribers\",\n    \"type\": \"CUSTOMER_LIST\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-custom-audiences/:id

Updates the MEMBER LIST, not the audience fields — the only mutation Reddit exposes (PATCH /custom_audiences/{id}/users). Body: {action_type:"ADD"|"REMOVE", users:[…]} with SHA-256-hashed emails/MAIDs. There is no rename.

Minimal body: {"action_type":"ADD","users":[{"email":"<sha256-hashed email>"}]}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action_type": "ADD",
    "users": [
        {
            "email": "<sha256-hashed email>"
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action_type": "ADD",
    "users": [
        {
            "email": "<sha256-hashed email>"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action_type": "ADD",
      "users": [
          {
              "email": "<sha256-hashed email>"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action_type\": \"ADD\",\n    \"users\": [\n        {\n            \"email\": \"<sha256-hashed email>\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/redditads/reddit-custom-audiences/:id

Delete a custom audience by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-custom-audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Data Deletion

GET/v1/redditads/reddit-data-deletion-jobs/:id

Status of a deletion job by id (from create).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-data-deletion-jobs

Submit an advertising-data deletion job (requires the adsdatadeletion scope). Required: ad_account_id; remaining fields forwarded per Reddit's deletion shape.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-data-deletion-jobs');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Forecasting

GET/v1/redditads/reddit-forecasts

Channel-planning reach estimate (GET /channel_planning/reach) — query params forwarded verbatim.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-forecasts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-forecasts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-forecasts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-forecasts

Generate a bid suggestion for a prospective ad group (POST /forecasting/bid_suggestions). Fields auto-wrapped as {data:{…}} and forwarded verbatim; returned bids in microcurrency.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-forecasts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-forecasts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-forecasts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Funding Instruments

GET/v1/redditads/reddit-funding-instruments?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

Payment sources. Required — exactly one of: ad_account_id (list the account's funding instruments — campaign create needs a funding_instrument_id) | funding_instrument_id (its child allocations).

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-funding-instruments?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-funding-instruments?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-funding-instruments?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-funding-instruments

This is a SEARCH, not creation — maps to POST /businesses/{business_id}/funding_instruments/query. Required: business_id; remaining fields are the filter body, forwarded verbatim.

Required body: business_id.

Minimal body: {"business_id":"{{reddit_business_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "business_id": "{{reddit_business_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-funding-instruments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "business_id": "{{reddit_business_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-funding-instruments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "business_id": "{{reddit_business_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-funding-instruments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"business_id\": \"{{reddit_business_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Lead Gen Forms

GET/v1/redditads/reddit-lead-gen-forms/:id

One lead gen form by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-lead-gen-forms?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List an ad account's lead generation forms. Required: ad_account_id.

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-lead-gen-forms

Create a lead gen form (immutable once created — no update/delete upstream). Required: ad_account_id.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Demo request form"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "Demo request form"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "name": "Demo request form"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-lead-gen-forms');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"name\": \"Demo request form\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Lookups

GET/v1/redditads/reddit-lookups?resource=time_zones

Parameterless reference lists. Required: resource ∈ industries (business verticals) | time_zones (for time_zone_id fields).

Required query: resource.

Minimal query: {"resource":"time_zones"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
resourcetime_zones
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-lookups?resource=time_zones' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-lookups?resource=time_zones', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-lookups?resource=time_zones');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Me

GET/v1/redditads/reddit-me

Takes no parameters — the smoke test after connecting. The authenticated Reddit member (GET /me).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-me' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-me', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-me');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Pixels + Conversions (CAPI)

GET/v1/redditads/reddit-pixels?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

Reddit Pixels. Required — exactly one of: ad_account_id | business_id (list pixels) | pixel_id (its last-fired-at firing diagnostics).

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-pixels?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-pixels?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-pixels?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-pixels

Send server-side conversion events (Conversions API) to a pixel — requires the adsconversions scope. Required: pixel_id; the events payload is auto-wrapped as {data:{…}} and forwarded verbatim (event_at, event_type, user, event_metadata per Reddit's CAPI shape).

Required body: pixel_id.

Minimal body: {"pixel_id":"{{reddit_pixel_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "pixel_id": "{{reddit_pixel_id}}",
    "events": [
        {
            "event_at": "2026-01-01T12:00:00Z",
            "event_type": {
                "tracking_type": "Purchase"
            }
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-pixels' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "pixel_id": "{{reddit_pixel_id}}",
    "events": [
        {
            "event_at": "2026-01-01T12:00:00Z",
            "event_type": {
                "tracking_type": "Purchase"
            }
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-pixels', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "pixel_id": "{{reddit_pixel_id}}",
      "events": [
          {
              "event_at": "2026-01-01T12:00:00Z",
              "event_type": {
                  "tracking_type": "Purchase"
              }
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-pixels');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"pixel_id\": \"{{reddit_pixel_id}}\",\n    \"events\": [\n        {\n            \"event_at\": \"2026-01-01T12:00:00Z\",\n            \"event_type\": {\n                \"tracking_type\": \"Purchase\"\n            }\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Posts

GET/v1/redditads/reddit-posts/:id

One post by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-posts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-posts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-posts?profile_id=%7B%7Breddit_profile_id%7D%7D

List a profile's posts — the post_id source for ad create. Required: profile_id (from reddit-profiles).

Required query: profile_id.

Minimal query: {"profile_id":"{{reddit_profile_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
profile_id{{reddit_profile_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-posts?profile_id=%7B%7Breddit_profile_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-posts?profile_id=%7B%7Breddit_profile_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-posts?profile_id=%7B%7Breddit_profile_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-posts

Create the (ad) post an ad promotes. Required: profile_id; flat fields auto-wrapped as {data:{…}} — headline, body, destination_url, media assets (upload via Reddit's creative-assets flow first for image/video posts).

Required body: profile_id.

Minimal body: {"profile_id":"{{reddit_profile_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "profile_id": "{{reddit_profile_id}}",
    "headline": "Meet the new Acme 3000",
    "type": "TEXT"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-posts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "profile_id": "{{reddit_profile_id}}",
    "headline": "Meet the new Acme 3000",
    "type": "TEXT"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "profile_id": "{{reddit_profile_id}}",
      "headline": "Meet the new Acme 3000",
      "type": "TEXT"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-posts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"profile_id\": \"{{reddit_profile_id}}\",\n    \"headline\": \"Meet the new Acme 3000\",\n    \"type\": \"TEXT\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-posts/:id

Update a post (PATCH upstream — send only changed fields).

Minimal body: {"headline":"Updated headline"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "headline": "Updated headline"
}
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-posts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "headline": "Updated headline"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-posts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "headline": "Updated headline"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"headline\": \"Updated headline\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Product Catalogs

GET/v1/redditads/reddit-product-catalogs/:id

One catalog by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-product-catalogs?business_id=%7B%7Breddit_business_id%7D%7D

List a business's product catalogs — the catalog_id source for feeds/sets/products. Required: business_id.

Required query: business_id.

Minimal query: {"business_id":"{{reddit_business_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
business_id{{reddit_business_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-catalogs?business_id=%7B%7Breddit_business_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-catalogs?business_id=%7B%7Breddit_business_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-catalogs?business_id=%7B%7Breddit_business_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-product-catalogs

Create a product catalog under a business. Required: business_id.

Required body: business_id.

Minimal body: {"business_id":"{{reddit_business_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "business_id": "{{reddit_business_id}}",
    "name": "Web store catalog"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-product-catalogs' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "business_id": "{{reddit_business_id}}",
    "name": "Web store catalog"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-catalogs', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "business_id": "{{reddit_business_id}}",
      "name": "Web store catalog"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-catalogs');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"business_id\": \"{{reddit_business_id}}\",\n    \"name\": \"Web store catalog\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-product-catalogs/:id

Update a catalog (PATCH upstream).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/redditads/reddit-product-catalogs/:id

Delete a catalog by id (its feeds/sets/products go with it).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-catalogs/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Product Feeds

GET/v1/redditads/reddit-product-feeds/:id

One feed by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-product-feeds?catalog_id=%7B%7Breddit_catalog_id%7D%7D

List a catalog's scheduled product feeds. Required: catalog_id (from reddit-product-catalogs).

Required query: catalog_id.

Minimal query: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
catalog_id{{reddit_catalog_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-feeds?catalog_id=%7B%7Breddit_catalog_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-feeds?catalog_id=%7B%7Breddit_catalog_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-feeds?catalog_id=%7B%7Breddit_catalog_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-product-feeds

Create a product feed (scheduled fetch of a product file). Required: catalog_id.

Required body: catalog_id.

Minimal body: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "catalog_id": "{{reddit_catalog_id}}",
    "name": "Daily feed"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-product-feeds' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "catalog_id": "{{reddit_catalog_id}}",
    "name": "Daily feed"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-feeds', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "catalog_id": "{{reddit_catalog_id}}",
      "name": "Daily feed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-feeds');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"catalog_id\": \"{{reddit_catalog_id}}\",\n    \"name\": \"Daily feed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-product-feeds/:id

Update a feed (PATCH upstream).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/redditads/reddit-product-feeds/:id

Delete a feed by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-feeds/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Product Sets

GET/v1/redditads/reddit-product-sets/:id

One product set by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-sets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-product-sets?catalog_id=%7B%7Breddit_catalog_id%7D%7D

List a catalog's product sets (filtered subsets ads can target). Required: catalog_id.

Required query: catalog_id.

Minimal query: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
catalog_id{{reddit_catalog_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-product-sets?catalog_id=%7B%7Breddit_catalog_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-sets?catalog_id=%7B%7Breddit_catalog_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-sets?catalog_id=%7B%7Breddit_catalog_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-product-sets

Create a product set in a catalog. Required: catalog_id.

Required body: catalog_id.

Minimal body: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "catalog_id": "{{reddit_catalog_id}}",
    "name": "Shoes under $100"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-product-sets' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "catalog_id": "{{reddit_catalog_id}}",
    "name": "Shoes under $100"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-sets', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "catalog_id": "{{reddit_catalog_id}}",
      "name": "Shoes under $100"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-sets');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"catalog_id\": \"{{reddit_catalog_id}}\",\n    \"name\": \"Shoes under \$100\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-product-sets/:id

Update a product set (PATCH upstream).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-product-sets/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/redditads/reddit-product-sets/:id

Delete a product set by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/redditads/reddit-product-sets/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-product-sets/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Products

GET/v1/redditads/reddit-products?catalog_id=%7B%7Breddit_catalog_id%7D%7D

List products. Required — exactly one of: catalog_id (all products in a catalog) | product_set_id (products in a set).

Minimal query: {"catalog_id":"{{reddit_catalog_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
catalog_id{{reddit_catalog_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-products?catalog_id=%7B%7Breddit_catalog_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-products?catalog_id=%7B%7Breddit_catalog_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-products?catalog_id=%7B%7Breddit_catalog_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-products

Batch-write products — Reddit has no per-product CRUD. Required: catalog_id + products (array of product objects). Default upserts (batch_upsert); fold action:"delete" to batch-delete the listed products instead.

Required body: catalog_id, products.

Minimal body: {"catalog_id":"{{reddit_catalog_id}}","products":[{"id":"sku-123","title":"Acme runner","price":"99.00 USD","link":"https://example.com/p/sku-123"}]}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "catalog_id": "{{reddit_catalog_id}}",
    "products": [
        {
            "id": "sku-123",
            "title": "Acme runner",
            "price": "99.00 USD",
            "link": "https://example.com/p/sku-123"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-products' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "catalog_id": "{{reddit_catalog_id}}",
    "products": [
        {
            "id": "sku-123",
            "title": "Acme runner",
            "price": "99.00 USD",
            "link": "https://example.com/p/sku-123"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "catalog_id": "{{reddit_catalog_id}}",
      "products": [
          {
              "id": "sku-123",
              "title": "Acme runner",
              "price": "99.00 USD",
              "link": "https://example.com/p/sku-123"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-products');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"catalog_id\": \"{{reddit_catalog_id}}\",\n    \"products\": [\n        {\n            \"id\": \"sku-123\",\n            \"title\": \"Acme runner\",\n            \"price\": \"99.00 USD\",\n            \"link\": \"https://example.com/p/sku-123\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Profiles

GET/v1/redditads/reddit-profiles/:id

One profile by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-profiles/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-profiles/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-profiles/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-profiles?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List profiles (the posting identities that own posts + creative assets). Required — exactly one of: ad_account_id | business_id. Each result's id is the profile_id the posts/structured-posts/creative-assets tools need.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-profiles?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-profiles?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-profiles?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Reports

POST/v1/redditads/reddit-reports

Performance reporting — a query-by-POST endpoint upstream, so it lives on create even though it only reads. Required: ad_account_id. Report definition fields (auto-wrapped as {data:{…}}): starts_at/ends_at (ISO 8601 UTC), breakdowns (e.g. ["campaign_id","date"]), fields (metrics: spend, impressions, clicks, ctr, ecpm, conversion metrics…), optional filter, time_zone_id. Spend comes back in microcurrency. Paginated (pagination.next_url).

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "starts_at": "2026-01-01T00:00:00Z",
    "ends_at": "2026-01-08T00:00:00Z",
    "breakdowns": [
        "campaign_id",
        "date"
    ],
    "fields": [
        "spend",
        "impressions",
        "clicks",
        "ctr"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-reports' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "starts_at": "2026-01-01T00:00:00Z",
    "ends_at": "2026-01-08T00:00:00Z",
    "breakdowns": [
        "campaign_id",
        "date"
    ],
    "fields": [
        "spend",
        "impressions",
        "clicks",
        "ctr"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-reports', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "starts_at": "2026-01-01T00:00:00Z",
      "ends_at": "2026-01-08T00:00:00Z",
      "breakdowns": [
          "campaign_id",
          "date"
      ],
      "fields": [
          "spend",
          "impressions",
          "clicks",
          "ctr"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-reports');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"starts_at\": \"2026-01-01T00:00:00Z\",\n    \"ends_at\": \"2026-01-08T00:00:00Z\",\n    \"breakdowns\": [\n        \"campaign_id\",\n        \"date\"\n    ],\n    \"fields\": [\n        \"spend\",\n        \"impressions\",\n        \"clicks\",\n        \"ctr\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Saved Audiences

GET/v1/redditads/reddit-saved-audiences/:id

One saved audience by id.

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-saved-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D

List an ad account's saved audiences (reusable targeting definitions). Required: ad_account_id.

Required query: ad_account_id.

Minimal query: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
ad_account_id{{reddit_ad_account_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-saved-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-saved-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-saved-audiences?ad_account_id=%7B%7Breddit_ad_account_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-saved-audiences

Create a saved audience — a named bundle of targeting (communities, interests, geos, devices; validate values with reddit-targeting first). Required: ad_account_id.

Required body: ad_account_id.

Minimal body: {"ad_account_id":"{{reddit_ad_account_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "US mobile gamers"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-saved-audiences' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "ad_account_id": "{{reddit_ad_account_id}}",
    "name": "US mobile gamers"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-saved-audiences', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "ad_account_id": "{{reddit_ad_account_id}}",
      "name": "US mobile gamers"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-saved-audiences');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"ad_account_id\": \"{{reddit_ad_account_id}}\",\n    \"name\": \"US mobile gamers\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-saved-audiences/:id

Update a saved audience (PATCH upstream).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-saved-audiences/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Structured Posts

GET/v1/redditads/reddit-structured-posts/:id

One structured post by id (once the creation job finished).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/redditads/reddit-structured-posts?profile_id=%7B%7Breddit_profile_id%7D%7D

Structured (multi-asset/carousel) posts. Required — exactly one of: profile_id (list the profile's structured posts) | job_id (poll an async creation job from create).

Minimal query: {"profile_id":"{{reddit_profile_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
profile_id{{reddit_profile_id}}
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-structured-posts?profile_id=%7B%7Breddit_profile_id%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-structured-posts?profile_id=%7B%7Breddit_profile_id%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-structured-posts?profile_id=%7B%7Breddit_profile_id%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-structured-posts

Create a structured post — async: submits a creation job and returns a job id; poll it via this tool's query verb with job_id until done. Required: profile_id; flat fields auto-wrapped as {data:{…}}.

Required body: profile_id.

Minimal body: {"profile_id":"{{reddit_profile_id}}"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "profile_id": "{{reddit_profile_id}}"
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-structured-posts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "profile_id": "{{reddit_profile_id}}"
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-structured-posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "profile_id": "{{reddit_profile_id}}"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-structured-posts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"profile_id\": \"{{reddit_profile_id}}\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/redditads/reddit-structured-posts/:id

Update a structured post (PATCH upstream).

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
[]
curl -X PUT 'https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '[]'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify([])
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-structured-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '[]');
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Reddit Ads — Targeting Reference

GET/v1/redditads/reddit-targeting?resource=communities_search&query=gaming

Targeting reference data — one tool for every GET /targeting/* endpoint. Required: resource ∈ carriers | communities | communities_search | communities_suggestions | devices | geolocations | interests | languages | third_party_audiences. Remaining query params forwarded verbatim (e.g. names for communities, query for communities_search).

Required query: resource.

Minimal query: {"resource":"communities_search"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

AuthorizationBearer YOUR_JWT_TOKEN
resourcecommunities_search
querygaming
curl -X GET 'https://api.endpointr.com/v1/redditads/reddit-targeting?resource=communities_search&query=gaming' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-targeting?resource=communities_search&query=gaming', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-targeting?resource=communities_search&query=gaming');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/redditads/reddit-targeting

Targeting validators/suggesters — one tool for every POST /targeting/* endpoint. Required: action ∈ geolocations_validations | keyword_suggestions | keyword_validations. Remaining fields auto-wrapped as {data:{…}} and forwarded verbatim.

Required body: action.

Minimal body: {"action":"keyword_validations"}

_Requires stored credentials: reddit-ads (PUT /v1/credentials/reddit-ads)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "keyword_validations",
    "keywords": [
        "running shoes"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/redditads/reddit-targeting' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "keyword_validations",
    "keywords": [
        "running shoes"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/redditads/reddit-targeting', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "keyword_validations",
      "keywords": [
          "running shoes"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/redditads/reddit-targeting');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"keyword_validations\",\n    \"keywords\": [\n        \"running shoes\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Rendering

HTML → PDF

POST/v1/rendering/html2-pdf

Render HTML or a URL to PDF via dompdf.

Minimal body: {"html":"<h1>Hello</h1>","paper_size":"A4","orientation":"portrait"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "html": "<h1>Hello</h1>",
    "paper_size": "A4",
    "orientation": "portrait"
}
curl -X POST 'https://api.endpointr.com/v1/rendering/html2-pdf' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "html": "<h1>Hello</h1>",
    "paper_size": "A4",
    "orientation": "portrait"
}'
const response = await fetch('https://api.endpointr.com/v1/rendering/html2-pdf', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "html": "<h1>Hello</h1>",
      "paper_size": "A4",
      "orientation": "portrait"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/rendering/html2-pdf');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"html\": \"<h1>Hello</h1>\",\n    \"paper_size\": \"A4\",\n    \"orientation\": \"portrait\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PDF page → PNG

POST/v1/rendering/pdf

Render one page of a remote PDF to PNG (requires Imagick extension).

Required body: url.

Minimal body: {"url":"https://example.com/doc.pdf"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com/doc.pdf",
    "page": 0
}
curl -X POST 'https://api.endpointr.com/v1/rendering/pdf' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com/doc.pdf",
    "page": 0
}'
const response = await fetch('https://api.endpointr.com/v1/rendering/pdf', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com/doc.pdf",
      "page": 0
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/rendering/pdf');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com/doc.pdf\",\n    \"page\": 0\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

PhantomJS

POST/v1/rendering/phantom-js

All actions throw. Documented body shape retained for reference. The website action is superseded by POST /v1/rendering/screenshot.

Minimal body: {"action":"website","url":"https://example.com","width":1280,"height":720}

Note: Stub — PhantomJS is deprecated. For screenshots use POST /v1/rendering/screenshot.

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "action": "website",
    "url": "https://example.com",
    "width": 1280,
    "height": 720
}
curl -X POST 'https://api.endpointr.com/v1/rendering/phantom-js' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "action": "website",
    "url": "https://example.com",
    "width": 1280,
    "height": 720
}'
const response = await fetch('https://api.endpointr.com/v1/rendering/phantom-js', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "action": "website",
      "url": "https://example.com",
      "width": 1280,
      "height": 720
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/rendering/phantom-js');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"action\": \"website\",\n    \"url\": \"https://example.com\",\n    \"width\": 1280,\n    \"height\": 720\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Screenshot / thumbnail

POST/v1/rendering/screenshot

Screenshot a live web page. Built for thumbnails and link previews — the default returns a hosted PNG URL you can drop straight into an <img>.

Requires SCRAPEDO_API_KEY. Rendering is done by scrape.do on a real browser; without the key this returns 501. This is the working replacement for the website action of the dead PhantomJS handler.

Request:
- url (required) — public http/https page. SSRF-validated.
- width / height — viewport in px, 240-3840. Default 1280x720.
- full_page — capture the whole scroll height instead of the viewport. Default false.
- selector — CSS selector to capture just one element. Beats full_page when both are sent.
- device — desktop (default) | mobile | tablet.
- thumbnail_width — downscale to this width before storing, aspect ratio preserved. Never upscales. Saves a round trip through /v1/graphics/resize.
- wait_ms — extra settle time after load, 0-15000. Use it for pages that animate in.
- geo_code — ISO-2 country for the proxy exit, e.g. dk. Defaults to SCRAPEDO_GEOCODE.
- super — residential/mobile proxy. Default false, deliberately: screenshots already force the rendered tier, and super multiplies the credit cost again. Turn it on only when a target actually blocks you.
- output — url (default) | base64 | both.

Response. {source_url, format, mime, bytes, width, height, full_page, via} plus url + expires_in + expires_at (for url/both) and image_base64 (for base64/both).

TTL. Hosted images live on the same 24-hour volume as /v1/images/host and are swept hourly, so the URL is good for 24-25 hours. There is no cache — each call re-shoots the page and spends credits. Cache the URL your side.

Errors. 400 — bad input (unknown output/device, out-of-range dimensions, non-public url); validated before anything is billed. 501 — SCRAPEDO_API_KEY is not set on this deployment. 502 — the render itself failed; the reason is verbatim in error (dead token, exhausted credits, blocked target, selector matched nothing) and scrape.do's own status is in upstream.status. The upstream status is never forwarded as the HTTP status: a dead proxy token returns 401 upstream, and echoing that would read as your endpointr auth having failed.

Other example bodies.

Full-page capture, no hosting:

{"url":"https://example.com","full_page":true,"output":"base64"}

Just the hero block, on a mobile viewport:

{"url":"https://example.com","selector":"#hero","device":"mobile"}

A stubborn page behind Cloudflare, given time to settle:

{"url":"https://example.com","super":true,"wait_ms":3000,"geo_code":"dk"}

Minimal body: {"url":"https://example.com","width":1280,"height":720,"thumbnail_width":400}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com",
    "width": 1280,
    "height": 720,
    "thumbnail_width": 400
}
curl -X POST 'https://api.endpointr.com/v1/rendering/screenshot' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com",
    "width": 1280,
    "height": 720,
    "thumbnail_width": 400
}'
const response = await fetch('https://api.endpointr.com/v1/rendering/screenshot', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com",
      "width": 1280,
      "height": 720,
      "thumbnail_width": 400
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/rendering/screenshot');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com\",\n    \"width\": 1280,\n    \"height\": 720,\n    \"thumbnail_width\": 400\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Scrapers

Header Markdown

GET/v1/scrapers/header-markdown-extractor

List resources.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/scrapers/header-markdown-extractor' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/scrapers/header-markdown-extractor', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/scrapers/header-markdown-extractor');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/scrapers/header-markdown-extractor?url=https%3A%2F%2Fexample.com

Extract h1-h6 headers from a URL as Markdown.

Required query: url.

Minimal query: {"url":"https://example.com"}

AuthorizationBearer YOUR_JWT_TOKEN
urlhttps://example.com
curl -X GET 'https://api.endpointr.com/v1/scrapers/header-markdown-extractor?url=https%3A%2F%2Fexample.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/scrapers/header-markdown-extractor?url=https%3A%2F%2Fexample.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/scrapers/header-markdown-extractor?url=https%3A%2F%2Fexample.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Search Console

Search Console — Search Analytics

POST/v1/searchconsole/search-analytics

Query search performance for a property — clicks, impressions, CTR, position — sliced by the dimensions you request.

Required: siteUrl (selects the property; pulled into the path), startDate, endDate (both YYYY-MM-DD).

Common optional fields (forwarded verbatim to Search Console): dimensions (query|page|country|device|date|searchAppearance), type (web|image|video|news|discover|googleNews), dimensionFilterGroups, rowLimit (max 25000), startRow, dataState (final|all).

Auth — hybrid (see Sites). Scope: webmasters.readonly.

Other example body — top pages last month with a refresh-triplet:

{
  "refresh_token":"{{google_refresh_token}}",
  "client_id":"{{google_client_id}}",
  "client_secret":"{{google_client_secret}}",
  "siteUrl":"sc-domain:example.com",
  "startDate":"2026-05-01",
  "endDate":"2026-05-31",
  "dimensions":["page"],
  "rowLimit":100
}

Required body: siteUrl, startDate, endDate.

Minimal body: {"siteUrl":"https://example.com/","startDate":"2026-05-01","endDate":"2026-05-28"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "siteUrl": "https://example.com/",
    "startDate": "2026-05-01",
    "endDate": "2026-05-28",
    "dimensions": [
        "query"
    ],
    "rowLimit": 25
}
curl -X POST 'https://api.endpointr.com/v1/searchconsole/search-analytics' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "siteUrl": "https://example.com/",
    "startDate": "2026-05-01",
    "endDate": "2026-05-28",
    "dimensions": [
        "query"
    ],
    "rowLimit": 25
}'
const response = await fetch('https://api.endpointr.com/v1/searchconsole/search-analytics', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "siteUrl": "https://example.com/",
      "startDate": "2026-05-01",
      "endDate": "2026-05-28",
      "dimensions": [
          "query"
      ],
      "rowLimit": 25
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/searchconsole/search-analytics');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"siteUrl\": \"https://example.com/\",\n    \"startDate\": \"2026-05-01\",\n    \"endDate\": \"2026-05-28\",\n    \"dimensions\": [\n        \"query\"\n    ],\n    \"rowLimit\": 25\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Search Console — Sitemaps

GET/v1/searchconsole/sitemaps?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F

List the sitemaps submitted for a property, or fetch one. Required: siteUrl. Add feedpath (the sitemap URL) to fetch a single sitemap's status instead of the list.

Auth — hybrid (see Sites). Scope: webmasters.readonly.

Single-sitemap query:

?oauth_token={{google_oauth_token}}&siteUrl=https://example.com/&feedpath=https://example.com/sitemap.xml

Required query: siteUrl.

Minimal query: {"siteUrl":"https://example.com/"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
siteUrlhttps://example.com/
curl -X GET 'https://api.endpointr.com/v1/searchconsole/sitemaps?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/searchconsole/sitemaps?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/searchconsole/sitemaps?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Search Console — Sites

GET/v1/searchconsole/sites

List the verified sites on the authenticated Google account.

Auth — hybrid. Store creds once via PUT /v1/credentials/google and send nothing; or carry the OAuth bag per-request (?oauth_token=… or the refresh-triplet). Scope: webmasters.readonly.

Response. {data: {siteEntry: [{siteUrl, permissionLevel}, …]}, refreshed_access_token?}.

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/searchconsole/sites' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/searchconsole/sites', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/searchconsole/sites');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/searchconsole/sites?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F

Fetch a single site by siteUrl. siteUrl carries slashes, so it's passed as a query param (not a path segment) — e.g. https://example.com/ or sc-domain:example.com.

Required query: siteUrl.

Minimal query: {"siteUrl":"https://example.com/"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
siteUrlhttps://example.com/
curl -X GET 'https://api.endpointr.com/v1/searchconsole/sites?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/searchconsole/sites?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/searchconsole/sites?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&siteUrl=https%3A%2F%2Fexample.com%2F');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Search Console — URL Inspection

POST/v1/searchconsole/url-inspection

Inspect a single URL's index status on a verified property — coverage verdict, last crawl time, indexing state, mobile usability, rich-results.

Required: inspectionUrl (the page to inspect), siteUrl (the verified property it belongs to). Optional: languageCode (BCP-47, e.g. en-US).

Auth — hybrid (see Sites). Scope: webmasters.readonly.

Required body: inspectionUrl, siteUrl.

Minimal body: {"inspectionUrl":"https://example.com/some-page","siteUrl":"https://example.com/"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "inspectionUrl": "https://example.com/some-page",
    "siteUrl": "https://example.com/"
}
curl -X POST 'https://api.endpointr.com/v1/searchconsole/url-inspection' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "inspectionUrl": "https://example.com/some-page",
    "siteUrl": "https://example.com/"
}'
const response = await fetch('https://api.endpointr.com/v1/searchconsole/url-inspection', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "inspectionUrl": "https://example.com/some-page",
      "siteUrl": "https://example.com/"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/searchconsole/url-inspection');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"inspectionUrl\": \"https://example.com/some-page\",\n    \"siteUrl\": \"https://example.com/\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Social

Leadshark — Automation status

PUT/v1/social/leadshark-automation-status/:id

Flip an automation's status by id (from the automations list) without a full update. Maps to PUT /api/automations/{id}/status.

Required body: status.

Minimal body: {"status":"Paused"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "status": "Paused"
}
curl -X PUT 'https://api.endpointr.com/v1/social/leadshark-automation-status/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "status": "Paused"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automation-status/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "status": "Paused"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automation-status/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"status\": \"Paused\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Automation templates

GET/v1/social/leadshark-automation-templates?page=1&limit=20

No params needed (page/limit optional; newest first). Each id is the template_id you reuse as automation.template_id in scheduled-posts (then name/dm_template become optional).

Minimal query: {"page":"1","limit":"20"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
limit20
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-automation-templates?page=1&limit=20' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automation-templates?page=1&limit=20', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automation-templates?page=1&limit=20');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Automations

GET/v1/social/leadshark-automations/:id

One automation by id (from the list), including its stats object.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-automations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automations/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/social/leadshark-automations?page=1&limit=10

List post-engagement automations (paginate with page, limit; each result carries a stats object). Each id feeds get/update/delete and the automation-status tool.

Minimal query: {"page":"1","limit":"10"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
limit10
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-automations?page=1&limit=10' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automations?page=1&limit=10', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automations?page=1&limit=10');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-automations

Create an automation bound to a LinkedIn post. Required: post_id (the full URN, e.g. urn:li:activity:7150…) + linkedin_post_url — BOTH come from the leadshark-posts tool (item.post_id + item.share_url). Use the full URN exactly (the numeric tail alone won't bind engagement). Optional: name, keywords[], dm_template, auto_connect, auto_like (Pro+/Apex), links_enabled, page_id (needs links_enabled), template_id, follow-up fields. Pass only {name, post_id, linkedin_post_url} for a webhook-only automation (comments stream to your webhooks; no LeadShark actions).

Required body: post_id, linkedin_post_url.

Minimal body: {"post_id":"urn:li:activity:7150123456789012345","linkedin_post_url":"https://www.linkedin.com/posts/..."}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Guide Giveaway",
    "post_id": "urn:li:activity:7150123456789012345",
    "linkedin_post_url": "https://www.linkedin.com/posts/...",
    "keywords": [
        "interested"
    ],
    "dm_template": "Hi {{firstName}}! Here is the guide: https://example.com/guide"
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-automations' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Guide Giveaway",
    "post_id": "urn:li:activity:7150123456789012345",
    "linkedin_post_url": "https://www.linkedin.com/posts/...",
    "keywords": [
        "interested"
    ],
    "dm_template": "Hi {{firstName}}! Here is the guide: https://example.com/guide"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automations', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Guide Giveaway",
      "post_id": "urn:li:activity:7150123456789012345",
      "linkedin_post_url": "https://www.linkedin.com/posts/...",
      "keywords": [
          "interested"
      ],
      "dm_template": "Hi {{firstName}}! Here is the guide: https://example.com/guide"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automations');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Guide Giveaway\",\n    \"post_id\": \"urn:li:activity:7150123456789012345\",\n    \"linkedin_post_url\": \"https://www.linkedin.com/posts/...\",\n    \"keywords\": [\n        \"interested\"\n    ],\n    \"dm_template\": \"Hi {{firstName}}! Here is the guide: https://example.com/guide\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/social/leadshark-automations/:id

Update an automation by id (from the list). Send only the fields to change.

Minimal body: {"keywords":["interested","yes"]}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "keywords": [
        "interested",
        "yes"
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/social/leadshark-automations/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "keywords": [
        "interested",
        "yes"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automations/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "keywords": [
          "interested",
          "yes"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"keywords\": [\n        \"interested\",\n        \"yes\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/social/leadshark-automations/:id

Delete an automation by id.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/social/leadshark-automations/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-automations/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-automations/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Bookmark tags

GET/v1/social/leadshark-bookmark-tags

Takes no parameters. All bookmark tags — the name/id source for filtering bookmarks.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-bookmark-tags' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-bookmark-tags', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-bookmark-tags');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Bookmarks

GET/v1/social/leadshark-bookmarks

List saved LinkedIn profile bookmarks (tags + notes). Filter tags come from the leadshark-bookmark-tags tool.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-bookmarks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-bookmarks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-bookmarks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-bookmarks

Save a bookmark (LinkedIn profile + optional tags/notes).

Minimal body: {"linkedin_url":"https://www.linkedin.com/in/john-doe/","tags":["prospect"],"note":"Met at conf"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "linkedin_url": "https://www.linkedin.com/in/john-doe/",
    "tags": [
        "prospect"
    ],
    "note": "Met at conf"
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-bookmarks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "linkedin_url": "https://www.linkedin.com/in/john-doe/",
    "tags": [
        "prospect"
    ],
    "note": "Met at conf"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-bookmarks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "linkedin_url": "https://www.linkedin.com/in/john-doe/",
      "tags": [
          "prospect"
      ],
      "note": "Met at conf"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-bookmarks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"linkedin_url\": \"https://www.linkedin.com/in/john-doe/\",\n    \"tags\": [\n        \"prospect\"\n    ],\n    \"note\": \"Met at conf\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/social/leadshark-bookmarks/:id

Delete a bookmark by id (from the list).

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/social/leadshark-bookmarks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-bookmarks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-bookmarks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Dashboard activity

GET/v1/social/leadshark-dashboard-activity?granularity=daily

Activity rollups (comments processed, DMs, connections, replies, leads) for exports. granularity: daily|weekly|monthly|all_time. after/before bound the window (ignored for all_time); optional cutoff returns before/on-after slices.

Minimal query: {"granularity":"daily"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
granularitydaily
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-dashboard-activity?granularity=daily' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-dashboard-activity?granularity=daily', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-dashboard-activity?granularity=daily');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Discover feed (Apex)

GET/v1/social/leadshark-discover

Apex. Fresh lead-magnet posts from the Lead Magnet Radar (public post + creator + engagement counts) — prospect-rich posts to engage.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-discover' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-discover', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-discover');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Enrich company

GET/v1/social/leadshark-enrich-company?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fcompany%2Facme%2F

Enrich a LinkedIn company. Pass the company identifier (company URL / handle), forwarded verbatim.

Minimal query: {"linkedin_url":"https://www.linkedin.com/company/acme/"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
linkedin_urlhttps://www.linkedin.com/company/acme/
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-enrich-company?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fcompany%2Facme%2F' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-enrich-company?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fcompany%2Facme%2F', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-enrich-company?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fcompany%2Facme%2F');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Enrich person

GET/v1/social/leadshark-enrich-person?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fjohn-doe%2F

Enrich a LinkedIn person (real profile view; soft cap ~200-250/day). Pass the profile identifier Leadshark expects (e.g. linkedin_url / profile URL or username), forwarded verbatim.

Minimal query: {"linkedin_url":"https://www.linkedin.com/in/john-doe/"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
linkedin_urlhttps://www.linkedin.com/in/john-doe/
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-enrich-person?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fjohn-doe%2F' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-enrich-person?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fjohn-doe%2F', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-enrich-person?linkedin_url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fjohn-doe%2F');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Leads

GET/v1/social/leadshark-leads?page=1&limit=250

List leads captured from automations and post engagement (email + ICP score when present). Non-archived individuals only. Page via page/limit and response.pagination.has_more.

Minimal query: {"page":"1","limit":"250"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
limit250
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-leads?page=1&limit=250' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-leads?page=1&limit=250', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-leads?page=1&limit=250');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — LinkedIn Posts

GET/v1/social/leadshark-posts?limit=25&page=1&linkedin_id=&raw=0

List your LinkedIn posts from Leadshark, returning {count, urls[]} (each post's share_url). Pass page + a small limit (e.g. 25) for one fast page — recommended. Omit page to auto-paginate all your posts in small pages (bounded to ~200; truncated:true if there are more). raw=1 returns full post objects (impressions/comments/reactions/reposts/created_at/…). linkedin_id targets another profile — omit it (don't send blank) for your own. Auth: vault leadshark default, OR per-request api_key passthrough.

Minimal query: {"page":"1","limit":"25"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
limit25
page1
linkedin_id
raw0
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-posts?limit=25&page=1&linkedin_id=&raw=0' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-posts?limit=25&page=1&linkedin_id=&raw=0', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-posts?limit=25&page=1&linkedin_id=&raw=0');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-linkedin-search

Run a LinkedIn people/company search. Body is forwarded verbatim to POST /api/linkedin-search.

Minimal body: {"keywords":"head of marketing","limit":25}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "keywords": "head of marketing",
    "limit": 25
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-linkedin-search' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "keywords": "head of marketing",
    "limit": 25
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-linkedin-search', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "keywords": "head of marketing",
      "limit": 25
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-linkedin-search');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"keywords\": \"head of marketing\",\n    \"limit\": 25\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Page emails (Pro+)

GET/v1/social/leadshark-page-emails?page_id=abc-123

Pro+. Deduped roll-up of every email captured for one Page across all its links (ideal for CRM export; use link-events for the raw per-link stream). page_id required. Each commenter_id joins to https://linkedin.com/in/{commenter_id}.

Required query: page_id.

Minimal query: {"page_id":"abc-123"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page_idabc-123
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-page-emails?page_id=abc-123' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-page-emails?page_id=abc-123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-page-emails?page_id=abc-123');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Page responses (Pro+)

GET/v1/social/leadshark-page-responses?page_id=abc-123

Pro+. One Page's quiz responses. page_id required (supports pagination).

Required query: page_id.

Minimal query: {"page_id":"abc-123"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page_idabc-123
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-page-responses?page_id=abc-123' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-page-responses?page_id=abc-123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-page-responses?page_id=abc-123');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Page stats (Pro+)

GET/v1/social/leadshark-page-stats?page_id=abc-123

Pro+. One Page's stats. page_id required (from the leadshark-pages tool).

Required query: page_id.

Minimal query: {"page_id":"abc-123"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page_idabc-123
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-page-stats?page_id=abc-123' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-page-stats?page_id=abc-123', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-page-stats?page_id=abc-123');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Pages (Pro+)

GET/v1/social/leadshark-pages/:id

One page by id (from the list).

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-pages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-pages/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-pages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/social/leadshark-pages

Pro+. List lead-magnet quiz Pages. Each id is the page_id for links / scheduled-post pre-automations and the page-stats/responses/emails tools.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-pages' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-pages', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-pages');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-pages

Create a Page. Required: title. Optional status (published), page_title, page_description, questions[] (each with answers mapped to fit|maybe|not). Save the returned page.id.

Required body: title.

Minimal body: {"title":"LinkedIn Growth Playbook"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "title": "LinkedIn Growth Playbook",
    "status": "published",
    "page_title": "Get Your Free Playbook",
    "page_description": "The exact strategies I used",
    "questions": [
        {
            "question_text": "Your biggest challenge?",
            "answers": [
                {
                    "answer_text": "Engagement",
                    "mapped_outcome": "fit"
                },
                {
                    "answer_text": "Just browsing",
                    "mapped_outcome": "not"
                }
            ]
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-pages' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "title": "LinkedIn Growth Playbook",
    "status": "published",
    "page_title": "Get Your Free Playbook",
    "page_description": "The exact strategies I used",
    "questions": [
        {
            "question_text": "Your biggest challenge?",
            "answers": [
                {
                    "answer_text": "Engagement",
                    "mapped_outcome": "fit"
                },
                {
                    "answer_text": "Just browsing",
                    "mapped_outcome": "not"
                }
            ]
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-pages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "title": "LinkedIn Growth Playbook",
      "status": "published",
      "page_title": "Get Your Free Playbook",
      "page_description": "The exact strategies I used",
      "questions": [
          {
              "question_text": "Your biggest challenge?",
              "answers": [
                  {
                      "answer_text": "Engagement",
                      "mapped_outcome": "fit"
                  },
                  {
                      "answer_text": "Just browsing",
                      "mapped_outcome": "not"
                  }
              ]
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-pages');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"title\": \"LinkedIn Growth Playbook\",\n    \"status\": \"published\",\n    \"page_title\": \"Get Your Free Playbook\",\n    \"page_description\": \"The exact strategies I used\",\n    \"questions\": [\n        {\n            \"question_text\": \"Your biggest challenge?\",\n            \"answers\": [\n                {\n                    \"answer_text\": \"Engagement\",\n                    \"mapped_outcome\": \"fit\"\n                },\n                {\n                    \"answer_text\": \"Just browsing\",\n                    \"mapped_outcome\": \"not\"\n                }\n            ]\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/social/leadshark-pages/:id

Update a Page by id.

Minimal body: {"page_description":"Updated"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "page_description": "Updated"
}
curl -X PUT 'https://api.endpointr.com/v1/social/leadshark-pages/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "page_description": "Updated"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-pages/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "page_description": "Updated"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-pages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"page_description\": \"Updated\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/social/leadshark-pages/:id

Delete a Page by id.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/social/leadshark-pages/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-pages/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-pages/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Post comments

GET/v1/social/leadshark-post-comments?post_id=urn%3Ali%3Aactivity%3A7150123456789012345

Comments on a post (live from LinkedIn; Pro & up). post_id required (LinkedIn activity URN from leadshark-posts). Page with cursor.

Required query: post_id.

Minimal query: {"post_id":"urn:li:activity:7150123456789012345"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
post_idurn:li:activity:7150123456789012345
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-post-comments?post_id=urn%3Ali%3Aactivity%3A7150123456789012345' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-post-comments?post_id=urn%3Ali%3Aactivity%3A7150123456789012345', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-post-comments?post_id=urn%3Ali%3Aactivity%3A7150123456789012345');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Post reactions

GET/v1/social/leadshark-post-reactions?post_id=urn%3Ali%3Aactivity%3A7150123456789012345

Who reacted to a post (live from LinkedIn; Pro & up). post_id required — a LinkedIn activity URN from the leadshark-posts tool (item.post_id) or post-stats social_id. Page with cursor until pagination.has_more is false.

Required query: post_id.

Minimal query: {"post_id":"urn:li:activity:7150123456789012345"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
post_idurn:li:activity:7150123456789012345
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-post-reactions?post_id=urn%3Ali%3Aactivity%3A7150123456789012345' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-post-reactions?post_id=urn%3Ali%3Aactivity%3A7150123456789012345', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-post-reactions?post_id=urn%3Ali%3Aactivity%3A7150123456789012345');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Post reposts

GET/v1/social/leadshark-post-reposts?post_id=urn%3Ali%3Aactivity%3A7150123456789012345

Who reposted a post (live from LinkedIn; Pro & up). post_id required (URN from leadshark-posts). ~10/page via cursor; space calls 1-2s apart.

Required query: post_id.

Minimal query: {"post_id":"urn:li:activity:7150123456789012345"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
post_idurn:li:activity:7150123456789012345
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-post-reposts?post_id=urn%3Ali%3Aactivity%3A7150123456789012345' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-post-reposts?post_id=urn%3Ali%3Aactivity%3A7150123456789012345', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-post-reposts?post_id=urn%3Ali%3Aactivity%3A7150123456789012345');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Post stats

GET/v1/social/leadshark-post-stats?summary=true

Post metrics. List mode is deprecated upstream (prefer the leadshark-posts tool). summary=true returns lifetime totals across your last N posts (Apex); max_posts caps the walk and next_cursor (as cursor) continues if truncated. Each post's social_id (URN) is the post_id the engagement tools take.

Minimal query: {"summary":"true"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
summarytrue
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-post-stats?summary=true' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-post-stats?summary=true', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-post-stats?summary=true');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Profile viewers (Apex)

GET/v1/social/leadshark-profile-viewers

Apex + Premium LinkedIn. Recent profile viewers. Page with cursor. Non-Apex keys 403; non-Premium LinkedIn 403s premium_required.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-profile-viewers' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-profile-viewers', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-profile-viewers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Scheduled posts

GET/v1/social/leadshark-scheduled-posts

List scheduled LinkedIn posts. Each id feeds update/delete.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-scheduled-posts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-scheduled-posts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-scheduled-posts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-scheduled-posts

Schedule a post. Required: content + scheduled_time (ISO 8601; 15 min-90 days out). Optional automation object (pre-automation) creates an automation on publish — it accepts page_id (from leadshark-pages) with links_enabled:true, or template_id (from leadshark-automation-templates). Use multipart/form-data for file attachments.

Required body: content, scheduled_time.

Minimal body: {"content":"Comment PLAYBOOK and I will DM it to you!","scheduled_time":"2026-04-25T14:00:00Z"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "content": "Comment PLAYBOOK and I will DM it to you!",
    "scheduled_time": "2026-04-25T14:00:00Z",
    "automation": {
        "name": "Playbook Giveaway",
        "keywords": [
            "playbook"
        ],
        "dm_template": "Hey {{firstName}}! Here: https://example.com/playbook",
        "links_enabled": true,
        "page_id": "abc-123"
    }
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-scheduled-posts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "content": "Comment PLAYBOOK and I will DM it to you!",
    "scheduled_time": "2026-04-25T14:00:00Z",
    "automation": {
        "name": "Playbook Giveaway",
        "keywords": [
            "playbook"
        ],
        "dm_template": "Hey {{firstName}}! Here: https://example.com/playbook",
        "links_enabled": true,
        "page_id": "abc-123"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-scheduled-posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "content": "Comment PLAYBOOK and I will DM it to you!",
      "scheduled_time": "2026-04-25T14:00:00Z",
      "automation": {
          "name": "Playbook Giveaway",
          "keywords": [
              "playbook"
          ],
          "dm_template": "Hey {{firstName}}! Here: https://example.com/playbook",
          "links_enabled": true,
          "page_id": "abc-123"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-scheduled-posts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"content\": \"Comment PLAYBOOK and I will DM it to you!\",\n    \"scheduled_time\": \"2026-04-25T14:00:00Z\",\n    \"automation\": {\n        \"name\": \"Playbook Giveaway\",\n        \"keywords\": [\n            \"playbook\"\n        ],\n        \"dm_template\": \"Hey {{firstName}}! Here: https://example.com/playbook\",\n        \"links_enabled\": true,\n        \"page_id\": \"abc-123\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/social/leadshark-scheduled-posts/:id

Edit a scheduled post by id (from the list). Cannot edit within 15 min of publish, or after it published.

Minimal body: {"content":"Updated copy"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "content": "Updated copy"
}
curl -X PUT 'https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "content": "Updated copy"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "content": "Updated copy"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"content\": \"Updated copy\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/social/leadshark-scheduled-posts/:id

Cancel a scheduled post by id.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-scheduled-posts/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Signal events (Apex)

GET/v1/social/leadshark-signal-events?page=1&type=comment

Apex. Raw engagement signal events. type: comment|reaction|repost|profile_view|lead_magnet_click|dm_sent|comment_reply|connection_accepted|connection_sent|automation_engagement. since/until filter by engagement date; since_captured/until_captured by when we detected it (best for what-is-new-this-week, especially reactions/reposts). total_pages pagination.

Minimal query: {"page":"1","type":"comment"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
typecomment
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-signal-events?page=1&type=comment' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-signal-events?page=1&type=comment', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-signal-events?page=1&type=comment');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Signals / hot leads (Apex)

GET/v1/social/leadshark-signals?page=1

Apex. Your ranked hot leads (deduped, heat-scored roll-up across all tracked posts). total_pages pagination — increment page until page == total_pages.

Minimal query: {"page":"1"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-signals?page=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-signals?page=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-signals?page=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-links

Create a tracking link. automation_id from leadshark-automations; page_id from leadshark-pages routes visitors through that Page.

Minimal body: {"url":"https://example.com/guide","automation_id":"...","page_id":"abc-123"}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "url": "https://example.com/guide",
    "automation_id": "...",
    "page_id": "abc-123"
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-links' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "url": "https://example.com/guide",
    "automation_id": "...",
    "page_id": "abc-123"
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-links', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "url": "https://example.com/guide",
      "automation_id": "...",
      "page_id": "abc-123"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-links');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"url\": \"https://example.com/guide\",\n    \"automation_id\": \"...\",\n    \"page_id\": \"abc-123\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Webhook test

POST/v1/social/leadshark-webhook-test

Send a test event to a webhook. webhook_id required (from the leadshark-webhooks tool). Maps to POST /api/v1/webhooks/{id}/test.

Required body: webhook_id.

Minimal body: {"webhook_id":"..."}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "webhook_id": "..."
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-webhook-test' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "webhook_id": "..."
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhook-test', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "webhook_id": "..."
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhook-test');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"webhook_id\": \"...\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Leadshark — Webhooks

GET/v1/social/leadshark-webhooks/:id

One webhook by id.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhooks/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/social/leadshark-webhooks

List webhooks. Each id feeds get/update/delete and the webhook-test tool.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/social/leadshark-webhooks' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhooks', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/social/leadshark-webhooks

Create a webhook (HMAC-SHA256 signed deliveries). event_types: new_comment, email_captured, lead_sent (Pro) + new_profile_visit, new_like (Apex only — non-Apex 403 apex_only_event_types). The signing secret is returned ONCE at creation.

Minimal body: {"target_url":"https://api.example.com/hooks/leadshark","event_types":["new_comment","email_captured"]}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "target_url": "https://api.example.com/hooks/leadshark",
    "event_types": [
        "new_comment",
        "email_captured"
    ]
}
curl -X POST 'https://api.endpointr.com/v1/social/leadshark-webhooks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "target_url": "https://api.example.com/hooks/leadshark",
    "event_types": [
        "new_comment",
        "email_captured"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhooks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "target_url": "https://api.example.com/hooks/leadshark",
      "event_types": [
          "new_comment",
          "email_captured"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhooks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"target_url\": \"https://api.example.com/hooks/leadshark\",\n    \"event_types\": [\n        \"new_comment\",\n        \"email_captured\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/social/leadshark-webhooks/:id

Update a webhook by id. Pass {rotate_secret:true} to rotate the signing secret (returned once in the response).

Minimal body: {"event_types":["new_comment"]}

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "event_types": [
        "new_comment"
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/social/leadshark-webhooks/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "event_types": [
        "new_comment"
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhooks/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "event_types": [
          "new_comment"
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"event_types\": [\n        \"new_comment\"\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/social/leadshark-webhooks/:id

Delete a webhook by id.

_Requires stored credentials: leadshark (PUT /v1/credentials/leadshark)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/social/leadshark-webhooks/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/social/leadshark-webhooks/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/social/leadshark-webhooks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Tasks

Google Tasks — Tasklists

GET/v1/tasks/google-tasklists/:id

Fetch a single tasklist by id. Auth in query (?oauth_token=… or refresh triplet).

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tasks/google-tasklists/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasklists/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasklists/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tasks/google-tasklists?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D

List the authenticated Google account's task lists.

Auth — hybrid (vault OR per-request).

Vault mode (recommended). Store creds once via PUT /v1/credentials/google with {client_id, client_secret, refresh_token} (or use the admin UI). Then *omit credentials from the request entirely* — Endpointr will mint a fresh access_token from the stored refresh_token on every call:

# nothing to add to the URL — the vault provides creds

Per-request mode. Carry the OAuth bag on every call. Useful when the credentials live in a client-side store (mobile/browser) rather than the server.

1. Direct access_token (cheapest — skips the token exchange):

?oauth_token={{google_oauth_token}}

2. Refresh-token triplet — Endpointr exchanges it via https://oauth2.googleapis.com/token, uses the access_token for the upstream call, and surfaces the new access_token in data.refreshed_access_token so the client can cache it:

?refresh_token={{google_refresh_token}}&client_id={{google_client_id}}&client_secret={{google_client_secret}}

Resolution order. Per-request oauth_token → per-request refresh-triplet → vault refresh-triplet → 400.

Required OAuth scope: https://www.googleapis.com/auth/tasks.

Response. {data: <google-payload>, refreshed_access_token?: <new>, expires_in?: <seconds>, scope?: <granted>}. Unwrap data to get the upstream Google body.

Minimal query: {"oauth_token":"{{google_oauth_token}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
curl -X GET 'https://api.endpointr.com/v1/tasks/google-tasklists?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasklists?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasklists?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tasks/google-tasklists

Create a new tasklist. Auth in body alongside the upstream fields.

Other example bodies.

Using refresh-triplet (no fresh access_token on hand):

{
  "refresh_token":"{{google_refresh_token}}",
  "client_id":"{{google_client_id}}",
  "client_secret":"{{google_client_secret}}",
  "title":"My new list"
}

Required body: title.

Minimal body: {"title":"My new list"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "title": "My new list"
}
curl -X POST 'https://api.endpointr.com/v1/tasks/google-tasklists' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "title": "My new list"
}'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasklists', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "title": "My new list"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasklists');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"title\": \"My new list\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tasks/google-tasklists/:id

Update a tasklist (PATCH upstream). Only the fields you send are touched.

Minimal body: {"oauth_token":"{{google_oauth_token}}","title":"Renamed list"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "title": "Renamed list"
}
curl -X PUT 'https://api.endpointr.com/v1/tasks/google-tasklists/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "title": "Renamed list"
}'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasklists/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "title": "Renamed list"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasklists/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"title\": \"Renamed list\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tasks/google-tasklists/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D

Delete a tasklist. Auth in query string.

Minimal query: {"oauth_token":"{{google_oauth_token}}"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
curl -X DELETE 'https://api.endpointr.com/v1/tasks/google-tasklists/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasklists/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasklists/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Google Tasks — Tasks

GET/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default

Fetch a single task. Required: tasklist + auth in query.

Minimal query: {"oauth_token":"{{google_oauth_token}}","tasklist":"@default"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
tasklist@default
curl -X GET 'https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tasks/google-tasks?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default&showCompleted=false

List tasks within a tasklist. Required: tasklist (use @default for the user's primary list, or any id from /v1/tasks/google-tasklists).

Auth — hybrid. See *Google Tasks — Tasklists* for the full resolution order. Short version: store creds once at /v1/credentials/google and tasklist is the only required field below; or carry the OAuth bag per-request as shown.

Forwarded filter params: showCompleted, showHidden, showDeleted, dueMin, dueMax, completedMin, completedMax, updatedMin, maxResults, pageToken.

Other example queries.

Open tasks only, due in the next 7 days:

?oauth_token={{google_oauth_token}}&tasklist=@default&showCompleted=false&dueMax=2026-05-05T23:59:59.000Z

Next page (cursor from previous response's nextPageToken):

?oauth_token={{google_oauth_token}}&tasklist=@default&pageToken=<from-prev-response>

Required query: tasklist.

Minimal query: {"tasklist":"@default"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
tasklist@default
showCompletedfalse
curl -X GET 'https://api.endpointr.com/v1/tasks/google-tasks?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default&showCompleted=false' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasks?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default&showCompleted=false', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasks?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default&showCompleted=false');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tasks/google-tasks

Create a task in a tasklist. Required: tasklist, title.

Optional fields: notes, due (RFC3339 timestamp — Google ignores time-of-day, only the date part counts), status (needsAction | completed), parent (sub-task), previous (insert position).

Other example bodies.

Sub-task under a parent:

{
  "oauth_token":"{{google_oauth_token}}",
  "tasklist":"@default",
  "parent":"<parent-task-id>",
  "title":"Whole milk",
  "notes":"2L"
}

Using the refresh triplet (no fresh access_token):

{
  "refresh_token":"{{google_refresh_token}}",
  "client_id":"{{google_client_id}}",
  "client_secret":"{{google_client_secret}}",
  "tasklist":"@default",
  "title":"Buy milk",
  "due":"2026-05-01T00:00:00.000Z"
}

Required body: tasklist, title.

Minimal body: {"tasklist":"@default","title":"Buy milk"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "tasklist": "@default",
    "title": "Buy milk",
    "notes": "Whole milk, 2L",
    "due": "2026-05-01T00:00:00.000Z"
}
curl -X POST 'https://api.endpointr.com/v1/tasks/google-tasks' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "tasklist": "@default",
    "title": "Buy milk",
    "notes": "Whole milk, 2L",
    "due": "2026-05-01T00:00:00.000Z"
}'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasks', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "tasklist": "@default",
      "title": "Buy milk",
      "notes": "Whole milk, 2L",
      "due": "2026-05-01T00:00:00.000Z"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasks');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"tasklist\": \"@default\",\n    \"title\": \"Buy milk\",\n    \"notes\": \"Whole milk, 2L\",\n    \"due\": \"2026-05-01T00:00:00.000Z\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/tasks/google-tasks/:id

Update a task (PATCH upstream — partial; only sent fields change). Required: tasklist.

Common updates:
- status: "completed" → check it off (Google also stamps completed automatically)
- status: "needsAction" → un-check
- due → reschedule
- title / notes → edit text

Other example bodies.

Un-check a previously completed task:

{"oauth_token":"{{google_oauth_token}}","tasklist":"@default","status":"needsAction"}

Reschedule:

{"oauth_token":"{{google_oauth_token}}","tasklist":"@default","due":"2026-05-08T00:00:00.000Z"}

Required body: tasklist.

Minimal body: {"tasklist":"@default"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "oauth_token": "{{google_oauth_token}}",
    "tasklist": "@default",
    "status": "completed"
}
curl -X PUT 'https://api.endpointr.com/v1/tasks/google-tasks/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "oauth_token": "{{google_oauth_token}}",
    "tasklist": "@default",
    "status": "completed"
}'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasks/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "oauth_token": "{{google_oauth_token}}",
      "tasklist": "@default",
      "status": "completed"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasks/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"oauth_token\": \"{{google_oauth_token}}\",\n    \"tasklist\": \"@default\",\n    \"status\": \"completed\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default

Delete a task. Required: tasklist + auth in query.

Minimal query: {"oauth_token":"{{google_oauth_token}}","tasklist":"@default"}

AuthorizationBearer YOUR_JWT_TOKEN
oauth_token{{google_oauth_token}}
tasklist@default
curl -X DELETE 'https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tasks/google-tasks/:id?oauth_token=%7B%7Bgoogle_oauth_token%7D%7D&tasklist=%40default');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal

TidyCal — Account (me)

GET/v1/tidycal/tidy-cal-me

Your TidyCal account profile (GET /me): name, email, timezone, language, vanity path, currency symbol and lifetime-pro status. No parameters. Multi-account: pass account to choose which stored credential to read.

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-me' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-me', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-me');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Accounts (labels)

GET/v1/tidycal/tidy-cal-accounts

List the TidyCal account labels configured for this customer (stored as tidycal:<label>). Returns {accounts:[…], has_default:bool} — never secrets. Pass one of these labels as the account selector on the other TidyCal tools. This tool takes no account selector.

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-accounts' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-accounts', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-accounts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Booking Types

GET/v1/tidycal/tidy-cal-booking-types?page=1

List your booking types — the scheduling pages you offer (GET /booking-types). Only page is supported. Each type's id is the booking_type_id the bookings + timeslots tools need.

Minimal query: {"page":"1"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types?page=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types?page=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types?page=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tidycal/tidy-cal-booking-types

Create a booking type (POST /booking-types).

Required: title, description (HTML allowed), duration_minutes, url_slug.

Optional: padding_minutes, latest_availability_days, private, max_bookings, booking_availability_interval_minutes, booking_threshold (min notice, minutes), redirect_url, price + payment_platform (stripe|paypal|tidycal, required when price>0) + currency_code.

Minimal (free):

{"title":"30 Minute Meeting","description":"Book a 30 minute meeting with me","duration_minutes":30,"url_slug":"30-minute-meeting"}

Paid:
{"title":"Consult","description":"Paid consult","duration_minutes":60,"url_slug":"consult","price":25,"payment_platform":"stripe","currency_code":"USD"}

Required body: title, description, duration_minutes, url_slug.

Minimal body: {"title":"30 Minute Meeting","description":"Book a 30 minute meeting with me","duration_minutes":30,"url_slug":"30-minute-meeting"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "title": "30 Minute Meeting",
    "description": "Book a 30 minute meeting with me",
    "duration_minutes": 30,
    "url_slug": "30-minute-meeting"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "title": "30 Minute Meeting",
    "description": "Book a 30 minute meeting with me",
    "duration_minutes": 30,
    "url_slug": "30-minute-meeting"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "title": "30 Minute Meeting",
      "description": "Book a 30 minute meeting with me",
      "duration_minutes": 30,
      "url_slug": "30-minute-meeting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-types');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"title\": \"30 Minute Meeting\",\n    \"description\": \"Book a 30 minute meeting with me\",\n    \"duration_minutes\": 30,\n    \"url_slug\": \"30-minute-meeting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Bookings

GET/v1/tidycal/tidy-cal-bookings/:id

Fetch one booking by its numeric {id} (booking type, contact, questions, payment, hosts).

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-bookings/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tidycal/tidy-cal-bookings?starts_at=2026-08-01&ends_at=2026-08-31

List your bookings (GET /bookings). All filters optional.

Filters: starts_at, ends_at (dates), cancelled (true = only cancelled), page, include_teams (true = include team bookings).

List a window:

?starts_at=2026-08-01&ends_at=2026-08-31

Minimal query: {"starts_at":"2026-08-01","ends_at":"2026-08-31"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
starts_at2026-08-01
ends_at2026-08-31
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-bookings?starts_at=2026-08-01&ends_at=2026-08-31' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings?starts_at=2026-08-01&ends_at=2026-08-31', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings?starts_at=2026-08-01&ends_at=2026-08-31');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tidycal/tidy-cal-bookings

Book a slot on a booking type (POSTs to /booking-types/{booking_type_id}/bookings). booking_type_id is required in the body — get one from the booking-types tool and a valid starts_at from the timeslots tool.

Required: booking_type_id, name, email, timezone, and a time — either starts_at (single) or a bookings array (package/multi-session).

Minimal (single):

{"booking_type_id":12345,"starts_at":"2026-08-13T10:00:00Z","name":"John Doe","email":"john@example.com","timezone":"America/Los_Angeles"}

With answers to booking questions:
{"booking_type_id":12345,"starts_at":"2026-08-13T10:00:00Z","name":"John Doe","email":"john@example.com","timezone":"America/Los_Angeles","booking_questions":[{"booking_type_question_id":1,"answer":"My answer"}]}

Package (multiple sessions):
{"booking_type_id":12345,"name":"John Doe","email":"john@example.com","timezone":"America/Los_Angeles","bookings":[{"starts_at":"2026-08-13T10:00:00Z"},{"starts_at":"2026-08-20T10:00:00Z"}]}

Required body: booking_type_id, name, email, timezone.

Minimal body: {"booking_type_id":12345,"name":"John Doe","email":"john@example.com","timezone":"America/Los_Angeles"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "booking_type_id": 12345,
    "starts_at": "2026-08-13T10:00:00Z",
    "name": "John Doe",
    "email": "john@example.com",
    "timezone": "America/Los_Angeles"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-bookings' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "booking_type_id": 12345,
    "starts_at": "2026-08-13T10:00:00Z",
    "name": "John Doe",
    "email": "john@example.com",
    "timezone": "America/Los_Angeles"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "booking_type_id": 12345,
      "starts_at": "2026-08-13T10:00:00Z",
      "name": "John Doe",
      "email": "john@example.com",
      "timezone": "America/Los_Angeles"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-bookings');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"booking_type_id\": 12345,\n    \"starts_at\": \"2026-08-13T10:00:00Z\",\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\",\n    \"timezone\": \"America/Los_Angeles\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Cancel booking

POST/v1/tidycal/tidy-cal-booking-cancel

Cancel a booking (PATCH /bookings/{booking_id}/cancel). booking_id is required; reason optional.

Minimal:

{"booking_id":98765}

With a reason:
{"booking_id":98765,"reason":"Client requested cancellation"}

Required body: booking_id.

Minimal body: {"booking_id":98765}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "booking_id": 98765,
    "reason": "Client requested cancellation"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-booking-cancel' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "booking_id": 98765,
    "reason": "Client requested cancellation"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-cancel', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "booking_id": 98765,
      "reason": "Client requested cancellation"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-booking-cancel');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"booking_id\": 98765,\n    \"reason\": \"Client requested cancellation\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Contacts

GET/v1/tidycal/tidy-cal-contacts?page=1

List your contacts (GET /contacts). Only page is supported.

Minimal query: {"page":"1"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-contacts?page=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-contacts?page=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-contacts?page=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tidycal/tidy-cal-contacts

Create a contact (POST /contacts). Required: name, email. Optional timezone. (TidyCal gates contact creation behind a lifetime subscription — HTTP 402 otherwise.)

Minimal:

{"name":"John Doe","email":"john@example.com"}

Required body: name, email.

Minimal body: {"name":"John Doe","email":"john@example.com"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "John Doe",
    "email": "john@example.com",
    "timezone": "America/New_York"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-contacts' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "John Doe",
    "email": "john@example.com",
    "timezone": "America/New_York"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-contacts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "John Doe",
      "email": "john@example.com",
      "timezone": "America/New_York"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-contacts');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"John Doe\",\n    \"email\": \"john@example.com\",\n    \"timezone\": \"America/New_York\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Team booking types

GET/v1/tidycal/tidy-cal-team-booking-types?team_id=42

List a team's booking types (GET /teams/{team_id}/booking-types). team_id is required. Optional page.

Required query: team_id.

Minimal query: {"team_id":"42"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
team_id42
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types?team_id=42' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types?team_id=42', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types?team_id=42');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tidycal/tidy-cal-team-booking-types

Create a booking type for a team (POST /teams/{team_id}/booking-types). Required: team_id, title, description, duration_minutes, url_slug (the tidy-cal-booking-types fields plus team_id).

Minimal:

{"team_id":42,"title":"Team Intro","description":"Meet the team","duration_minutes":30,"url_slug":"team-intro"}

Required body: team_id, title, description, duration_minutes, url_slug.

Minimal body: {"team_id":42,"title":"Team Intro","description":"Meet the team","duration_minutes":30,"url_slug":"team-intro"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "team_id": 42,
    "title": "Team Intro",
    "description": "Meet the team",
    "duration_minutes": 30,
    "url_slug": "team-intro"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "team_id": 42,
    "title": "Team Intro",
    "description": "Meet the team",
    "duration_minutes": 30,
    "url_slug": "team-intro"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "team_id": 42,
      "title": "Team Intro",
      "description": "Meet the team",
      "duration_minutes": 30,
      "url_slug": "team-intro"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-booking-types');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"team_id\": 42,\n    \"title\": \"Team Intro\",\n    \"description\": \"Meet the team\",\n    \"duration_minutes\": 30,\n    \"url_slug\": \"team-intro\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Team bookings

GET/v1/tidycal/tidy-cal-team-bookings?team_id=42&start_date=2026-08-01&end_date=2026-08-31

List a team's bookings (GET /teams/{team_id}/bookings). team_id is required. Optional filters: page, start_date, end_date, email, host_id, cancelled (true/false).

Minimal:

?team_id=42

Required query: team_id.

Minimal query: {"team_id":"42"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
team_id42
start_date2026-08-01
end_date2026-08-31
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-bookings?team_id=42&start_date=2026-08-01&end_date=2026-08-31' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-bookings?team_id=42&start_date=2026-08-01&end_date=2026-08-31', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-bookings?team_id=42&start_date=2026-08-01&end_date=2026-08-31');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Team users

GET/v1/tidycal/tidy-cal-team-users?team_id=42

List a team's users (GET /teams/{team_id}/users). team_id is required. Optional page.

Required query: team_id.

Minimal query: {"team_id":"42"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
team_id42
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-users?team_id=42' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users?team_id=42', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users?team_id=42');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/tidycal/tidy-cal-team-users

Invite a user to a team by email (POST /teams/{team_id}/users). Required: team_id, email. Optional role_name (admin|user).

Minimal:

{"team_id":42,"email":"user@example.com"}

As an admin:
{"team_id":42,"email":"user@example.com","role_name":"admin"}

Required body: team_id, email.

Minimal body: {"team_id":42,"email":"user@example.com"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "team_id": 42,
    "email": "user@example.com",
    "role_name": "user"
}
curl -X POST 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-users' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "team_id": 42,
    "email": "user@example.com",
    "role_name": "user"
}'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "team_id": 42,
      "email": "user@example.com",
      "role_name": "user"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"team_id\": 42,\n    \"email\": \"user@example.com\",\n    \"role_name\": \"user\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/tidycal/tidy-cal-team-users/:id

Remove a user from a team (DELETE /teams/{team_id}/users/{teamUser}). Id is the composite teamId:teamUserId — over MCP the team can't travel in the query.

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/tidycal/tidy-cal-team-users/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-team-users/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Teams

GET/v1/tidycal/tidy-cal-teams/:id

Fetch one team by its numeric {id}.

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-teams/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-teams/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-teams/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/tidycal/tidy-cal-teams?page=1

List teams you own or belong to (GET /teams). Only page is supported. Each team's id is used by the team-bookings / team-users / team-booking-types tools.

Minimal query: {"page":"1"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
page1
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-teams?page=1' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-teams?page=1', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-teams?page=1');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

TidyCal — Timeslots (availability)

GET/v1/tidycal/tidy-cal-timeslots?booking_type_id=12345&starts_at=2026-08-13T00%3A00%3A00Z&ends_at=2026-08-20T00%3A00%3A00Z

Get bookable time slots for a booking type over a UTC range (GET /booking-types/{booking_type_id}/timeslots). Honors your schedule, existing bookings, calendar conflicts and buffers.

Required: booking_type_id, starts_at, ends_at (UTC ISO-8601).

Minimal:

?booking_type_id=12345&starts_at=2026-08-13T00:00:00Z&ends_at=2026-08-20T00:00:00Z

Required query: booking_type_id, starts_at, ends_at.

Minimal query: {"booking_type_id":"12345","starts_at":"2026-08-13T00:00:00Z","ends_at":"2026-08-20T00:00:00Z"}

_Requires stored credentials: tidycal (PUT /v1/credentials/tidycal)._

AuthorizationBearer YOUR_JWT_TOKEN
booking_type_id12345
starts_at2026-08-13T00:00:00Z
ends_at2026-08-20T00:00:00Z
curl -X GET 'https://api.endpointr.com/v1/tidycal/tidy-cal-timeslots?booking_type_id=12345&starts_at=2026-08-13T00%3A00%3A00Z&ends_at=2026-08-20T00%3A00%3A00Z' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/tidycal/tidy-cal-timeslots?booking_type_id=12345&starts_at=2026-08-13T00%3A00%3A00Z&ends_at=2026-08-20T00%3A00%3A00Z', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/tidycal/tidy-cal-timeslots?booking_type_id=12345&starts_at=2026-08-13T00%3A00%3A00Z&ends_at=2026-08-20T00%3A00%3A00Z');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting

Unified accounting — Accounting years

GET/v1/unified-accounting/accounting-years

The bound company's fiscal years as {id, name, from, to, closed, raw}. Billy synthesises them from the organisation's fiscal-year settings (partial).

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/accounting-years' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/accounting-years', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/accounting-years');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Accounts (chart, typed)

GET/v1/unified-accounting/accounts?include_hidden=

The bound company's chart of accounts with an ENGLISH type on every account (income, financial_income, cost_of_goods, expense, salary, depreciation, financial_cost, tax, vat, bank, receivable, payable, asset, liability, equity, other), side (result|balance), type_source, the vendor's own category, counts_as_spend, and raw. Read type, never guess from names. Overrides per call: account_types ({"3610":"salary"}), category_types, keywords, include_types; include_hidden=true lists archived accounts.

Minimal query: {"include_hidden":false}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
include_hidden
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/accounts?include_hidden=' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/accounts?include_hidden=', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/accounts?include_hidden=');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Attachments

POST/v1/unified-accounting/attachments

Attach a document to an invoice, a journal entry or a supplier invoice in one call: target_kind (invoice|journal_entry|supplier_invoice), target_id, file_name, content_base64, optional mime. The vendor's two-step upload-then-link is folded in.

Required body: target_kind, target_id, file_name, content_base64.

Minimal body: {"target_kind":"invoice","target_id":"<invoice-id>","file_name":"receipt.pdf","content_base64":"<base64>"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "target_kind": "invoice",
    "target_id": "<invoice-id>",
    "file_name": "receipt.pdf",
    "mime": "application/pdf",
    "content_base64": "<base64>"
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/attachments' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "target_kind": "invoice",
    "target_id": "<invoice-id>",
    "file_name": "receipt.pdf",
    "mime": "application/pdf",
    "content_base64": "<base64>"
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/attachments', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "target_kind": "invoice",
      "target_id": "<invoice-id>",
      "file_name": "receipt.pdf",
      "mime": "application/pdf",
      "content_base64": "<base64>"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/attachments');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"target_kind\": \"invoice\",\n    \"target_id\": \"<invoice-id>\",\n    \"file_name\": \"receipt.pdf\",\n    \"mime\": \"application/pdf\",\n    \"content_base64\": \"<base64>\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Capabilities

GET/v1/unified-accounting/capabilities?provider=billy

What the unified layer can do for a binding's provider, per canonical operation: supported | partial (read note) | unsupported (call the named vendor_tool on the vendor connector) | pending (adapter not built yet). With account (a binding label) or provider it answers for that provider; with neither, the matrix for all four providers. Read it before planning a workflow across providers.

Minimal query: {"provider":"billy"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
providerbilly
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/capabilities?provider=billy' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/capabilities?provider=billy', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/capabilities?provider=billy');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Companies (bindings)

GET/v1/unified-accounting/companies

List the company bindings configured for this customer: {companies:[{label, name, provider, account, connected}], account_required, providers, connected, how_to_bind}. A binding is a credential row unified-accounting:<label> {provider: dinero|billy|economic|uniconta, account: that provider's company label, account_name}. Pass a label as the account selector on every other unified tool; when more than one binding exists it is REQUIRED. This tool takes no selector.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/companies' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/companies', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/companies');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Connect (end-customer grant flow)

GET/v1/unified-accounting/connect?label=tenant-4711

The state of one label: {label, provider, bound, connected, company, organization_id|agreement_number, organisations?, hint?}. For a Dinero grant whose login reaches several organisations and none is chosen yet, organisations lists the choices.

Minimal query: {"label":"tenant-4711"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
labeltenant-4711
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/connect?label=tenant-4711' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/connect?label=tenant-4711', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/connect?label=tenant-4711');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/connect

Connect one of YOUR users' bookkeeping without the endpointr admin. {provider: dinero|economic, label, return_url[, account_name]} returns {url, expires_at, redirect_uri}: send the user to url; they log in and grant at Visma Connect (Dinero) or e-conomic's install page, and endpointr writes <provider>:<label> (their own grant, refreshed on that row) plus unified-accounting:<label> under this customer, then redirects them to return_url?status=connected&provider=…&label=…&company=…. Other outcomes: status=choose_organisation&organisations=<base64url JSON> (a Dinero login reaching several organisations — then call this tool with {action: "choose_organisation", label, organization_id}), status=error&reason=denied|no_code|no_token|no_app|exchange_failed|self_failed|failed. Needs your app on the base row: dinero {client_id, client_secret} or economic {app_secret_token, install_url}. Labels: lowercase, digits, - and _ (e.g. tenant-4711); reuse a label to re-grant. Billy and Uniconta have no grant page — PUT /v1/credentials/<provider>:<label> with their keys and PUT /v1/credentials/unified-accounting:<label> {provider, account: <label>, account_name} instead; for Uniconta store {username, password} on the label row, read this tool's query for that login's organisations, then PUT the chosen company_id. Links expire after 30 minutes.

Minimal body: {"provider":"dinero","label":"tenant-4711","account_name":"Example ApS","return_url":"https://app.example.com/settings/accounting"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "provider": "dinero",
    "label": "tenant-4711",
    "account_name": "Example ApS",
    "return_url": "https://app.example.com/settings/accounting"
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/connect' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "provider": "dinero",
    "label": "tenant-4711",
    "account_name": "Example ApS",
    "return_url": "https://app.example.com/settings/accounting"
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/connect', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "provider": "dinero",
      "label": "tenant-4711",
      "account_name": "Example ApS",
      "return_url": "https://app.example.com/settings/accounting"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/connect');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"provider\": \"dinero\",\n    \"label\": \"tenant-4711\",\n    \"account_name\": \"Example ApS\",\n    \"return_url\": \"https://app.example.com/settings/accounting\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Customers

GET/v1/unified-accounting/customers/:id

Get one customer by {id} (the vendor id).

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/customers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/customers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/unified-accounting/customers?email=billing%40example.com

List customers in the canonical contact shape {id, number, name, email, phone, vat_number, address{street,zip,city,country}, currency, payment_terms_days, is_person, provider, raw}. Answers {customers: [...], page, has_more}. q is the vendor's own free-text search and page/page_size page it. email, phone and vat_number are EXACT lookups, matched by endpointr on the canonical record on every provider (emails case-insensitively, phones by their digits with or without a country code, VAT numbers without punctuation or a leading DK) and answered on one page: use them to find out whether a customer already exists before creating one.

Minimal query: {"email":"billing@example.com"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
emailbilling@example.com
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/customers?email=billing%40example.com' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/customers?email=billing%40example.com', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/customers?email=billing%40example.com');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/customers

Create a customer. name required; the rest optional and mapped per provider: email is where the books mail the customer (on Billy it becomes the contact's primary contact person), address.country is ISO 3166 alpha-2 (Uniconta takes its own country names; a country endpointr cannot map is left to Uniconta's default).

Minimal body: {"name":"Example ApS","email":"billing@example.com","vat_number":"12345678","address":{"street":"Road 1","zip":"2100","city":"Copenhagen","country":"DK"},"payment_terms_days":8}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Example ApS",
    "email": "billing@example.com",
    "vat_number": "12345678",
    "address": {
        "street": "Road 1",
        "zip": "2100",
        "city": "Copenhagen",
        "country": "DK"
    },
    "payment_terms_days": 8
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/customers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Example ApS",
    "email": "billing@example.com",
    "vat_number": "12345678",
    "address": {
        "street": "Road 1",
        "zip": "2100",
        "city": "Copenhagen",
        "country": "DK"
    },
    "payment_terms_days": 8
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/customers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Example ApS",
      "email": "billing@example.com",
      "vat_number": "12345678",
      "address": {
          "street": "Road 1",
          "zip": "2100",
          "city": "Copenhagen",
          "country": "DK"
      },
      "payment_terms_days": 8
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/customers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Example ApS\",\n    \"email\": \"billing@example.com\",\n    \"vat_number\": \"12345678\",\n    \"address\": {\n        \"street\": \"Road 1\",\n        \"zip\": \"2100\",\n        \"city\": \"Copenhagen\",\n        \"country\": \"DK\"\n    },\n    \"payment_terms_days\": 8\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/unified-accounting/customers/:id

Update a customer by {id} (partial: only the fields sent change; a field not sent is never blanked).

Minimal body: {"email":"new@example.com"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "email": "new@example.com"
}
curl -X PUT 'https://api.endpointr.com/v1/unified-accounting/customers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "email": "new@example.com"
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/customers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "email": "new@example.com"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/customers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"email\": \"new@example.com\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Entries (ledger)

GET/v1/unified-accounting/entries?from=2026-09-01&to=2026-09-30&page_size=1000

The ledger for a period: every posted line as {date, account_number, account_name, amount, voucher_number, voucher_type, description, contact_id, raw}, DEBIT-POSITIVE on every provider (expenses positive, revenue negative). from and to required; account_from/account_to filter by account number; page/page_size page.

Required query: from, to.

Minimal query: {"from":"2026-09-01","to":"2026-09-30"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
from2026-09-01
to2026-09-30
page_size1000
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/entries?from=2026-09-01&to=2026-09-30&page_size=1000' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/entries?from=2026-09-01&to=2026-09-30&page_size=1000', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/entries?from=2026-09-01&to=2026-09-30&page_size=1000');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Invoices

GET/v1/unified-accounting/invoices/:id

Get one invoice by {id}.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/invoices/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/invoices/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/invoices/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/unified-accounting/invoices?from=2026-09-01&to=2026-09-30&status=booked

List sales invoices as {id, number, status draft|booked|sent|paid|overdue|credited, kind invoice|credit_note, customer_id, customer_name, date, due_date, currency, lines[{product_id, description, quantity, unit_price, account_number, vat_type_id, total_excl_vat}], total_excl_vat, vat, total_incl_vat, balance_due, credited_invoice_id, provider, raw}. Filters: from, to (invoice date), status, q, page, page_size. action=pdf (id) returns {mime, content_base64}.

Minimal query: {"from":"2026-09-01","to":"2026-09-30","status":"booked"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
from2026-09-01
to2026-09-30
statusbooked
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/invoices?from=2026-09-01&to=2026-09-30&status=booked' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/invoices?from=2026-09-01&to=2026-09-30&status=booked', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/invoices?from=2026-09-01&to=2026-09-30&status=booked');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/invoices

Create a DRAFT invoice: customer_id, date, lines (each description, quantity, unit_price, plus product_id and/or account_number; Billy needs product_id, Dinero takes either). Optional currency, due_date, description. Write sub-operations via action: book (id) books/approves the draft; send (id, to?, subject?, message?, contact_person_id?) emails it; credit_note (id, date?) credits it (Dinero returns a DRAFT credit note to book with the vendor tool); register_payment (id, amount, date, bank_account_number? | bank_account_id?, description?). Unsupported combinations fail with UNIFIED_UNSUPPORTED and vendor_tool.

Minimal body: {"customer_id":"<customer-id>","date":"2026-09-01","currency":"DKK","lines":[{"description":"Hosting september","quantity":1,"unit_price":800,"account_number":1000,"product_id":"<product-id>"}]}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "customer_id": "<customer-id>",
    "date": "2026-09-01",
    "currency": "DKK",
    "lines": [
        {
            "description": "Hosting september",
            "quantity": 1,
            "unit_price": 800,
            "account_number": 1000,
            "product_id": "<product-id>"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/invoices' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "customer_id": "<customer-id>",
    "date": "2026-09-01",
    "currency": "DKK",
    "lines": [
        {
            "description": "Hosting september",
            "quantity": 1,
            "unit_price": 800,
            "account_number": 1000,
            "product_id": "<product-id>"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/invoices', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "customer_id": "<customer-id>",
      "date": "2026-09-01",
      "currency": "DKK",
      "lines": [
          {
              "description": "Hosting september",
              "quantity": 1,
              "unit_price": 800,
              "account_number": 1000,
              "product_id": "<product-id>"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/invoices');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"customer_id\": \"<customer-id>\",\n    \"date\": \"2026-09-01\",\n    \"currency\": \"DKK\",\n    \"lines\": [\n        {\n            \"description\": \"Hosting september\",\n            \"quantity\": 1,\n            \"unit_price\": 800,\n            \"account_number\": 1000,\n            \"product_id\": \"<product-id>\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Journal entries (manual vouchers)

POST/v1/unified-accounting/journal-entries

Create a DRAFT manual entry: date, optional description, lines (each account_number, amount debit-positive, optional contra_account_number, vat_type_id, description). A line with a contra account is expanded into a balanced pair on vendors that take balanced line sets. Via action: book (id) posts the draft; simulate validates without posting where the vendor can (Uniconta). Returns {id, status, date, voucher_number, lines, provider, raw}.

Minimal body: {"date":"2026-09-30","description":"Correction","lines":[{"account_number":7320,"amount":100,"contra_account_number":55000,"description":"Correction"}]}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "date": "2026-09-30",
    "description": "Correction",
    "lines": [
        {
            "account_number": 7320,
            "amount": 100,
            "contra_account_number": 55000,
            "description": "Correction"
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/journal-entries' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "date": "2026-09-30",
    "description": "Correction",
    "lines": [
        {
            "account_number": 7320,
            "amount": 100,
            "contra_account_number": 55000,
            "description": "Correction"
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/journal-entries', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "date": "2026-09-30",
      "description": "Correction",
      "lines": [
          {
              "account_number": 7320,
              "amount": 100,
              "contra_account_number": 55000,
              "description": "Correction"
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/journal-entries');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"date\": \"2026-09-30\",\n    \"description\": \"Correction\",\n    \"lines\": [\n        {\n            \"account_number\": 7320,\n            \"amount\": 100,\n            \"contra_account_number\": 55000,\n            \"description\": \"Correction\"\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Products

GET/v1/unified-accounting/products/:id

Get one product by {id}.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/products/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/products/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/unified-accounting/products?page=0&page_size=100

List products as {products: [{id, number, name, description, unit_price, currency, unit, account_number, vat_type_id, provider, raw}], page, has_more}.

Minimal query: {"page":0,"page_size":100}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
page_size100
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/products?page=0&page_size=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/products?page=0&page_size=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/products?page=0&page_size=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/products

Create a product. name and unit_price required; account_number (the sales account) is required by Dinero and Billy.

Minimal body: {"name":"Hosting","unit_price":800,"account_number":1000,"unit":"parts"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting",
    "unit_price": 800,
    "account_number": 1000,
    "unit": "parts"
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/products' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting",
    "unit_price": 800,
    "account_number": 1000,
    "unit": "parts"
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/products', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting",
      "unit_price": 800,
      "account_number": 1000,
      "unit": "parts"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/products');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting\",\n    \"unit_price\": 800,\n    \"account_number\": 1000,\n    \"unit\": \"parts\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/unified-accounting/products/:id

Update a product by {id} (partial).

Minimal body: {"unit_price":900}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "unit_price": 900
}
curl -X PUT 'https://api.endpointr.com/v1/unified-accounting/products/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "unit_price": 900
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/products/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "unit_price": 900
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/products/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"unit_price\": 900\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Quotes (offers)

GET/v1/unified-accounting/quotes/:id

Get one quote by {id}, with its lines.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/quotes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/quotes/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/unified-accounting/quotes?page=0&page_size=100

List quotes (offers, tilbud) as {quotes: [{id, number, status, customer_id, customer_name, date, currency, lines, total_excl_vat, provider, raw}], page, has_more}. Mapped for Dinero (trade offers) and Uniconta (offers; needs its Order module). Billy has quotes but does not let a company's API access token reach them; for e-conomic use the vendor tools named by the capabilities tool.

Minimal query: {"page":0,"page_size":100}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
page_size100
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/quotes?page=0&page_size=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/quotes?page=0&page_size=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/quotes?page=0&page_size=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/quotes

Create a quote: customer_id and lines (each description, quantity, unit_price, optionally product_id) required; date, currency, description optional.

Minimal body: {"customer_id":"<customer-id>","date":"2026-10-02","lines":[{"description":"New roof, as agreed","quantity":1,"unit_price":48000}]}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "customer_id": "<customer-id>",
    "date": "2026-10-02",
    "lines": [
        {
            "description": "New roof, as agreed",
            "quantity": 1,
            "unit_price": 48000
        }
    ]
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/quotes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "customer_id": "<customer-id>",
    "date": "2026-10-02",
    "lines": [
        {
            "description": "New roof, as agreed",
            "quantity": 1,
            "unit_price": 48000
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/quotes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "customer_id": "<customer-id>",
      "date": "2026-10-02",
      "lines": [
          {
              "description": "New roof, as agreed",
              "quantity": 1,
              "unit_price": 48000
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/quotes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"customer_id\": \"<customer-id>\",\n    \"date\": \"2026-10-02\",\n    \"lines\": [\n        {\n            \"description\": \"New roof, as agreed\",\n            \"quantity\": 1,\n            \"unit_price\": 48000\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/unified-accounting/quotes/:id

Replace a quote by {id}: send the WHOLE quote again, lines too. The quote that comes back may carry ANOTHER id than the one given (Uniconta deletes the offer and makes it again): keep the id returned.

Minimal body: {"lines":[{"description":"New roof, with gutters","quantity":1,"unit_price":52000}]}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "lines": [
        {
            "description": "New roof, with gutters",
            "quantity": 1,
            "unit_price": 52000
        }
    ]
}
curl -X PUT 'https://api.endpointr.com/v1/unified-accounting/quotes/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "lines": [
        {
            "description": "New roof, with gutters",
            "quantity": 1,
            "unit_price": 52000
        }
    ]
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/quotes/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "lines": [
          {
              "description": "New roof, with gutters",
              "quantity": 1,
              "unit_price": 52000
          }
      ]
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"lines\": [\n        {\n            \"description\": \"New roof, with gutters\",\n            \"quantity\": 1,\n            \"unit_price\": 52000\n        }\n    ]\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
DELETE/v1/unified-accounting/quotes/:id

Delete a quote by {id}. A quote that is already gone answers {deleted: true}.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X DELETE 'https://api.endpointr.com/v1/unified-accounting/quotes/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/quotes/:id', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/quotes/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Readiness (can this company issue an invoice)

GET/v1/unified-accounting/readiness

Whether the bound company's books have what issuing an invoice needs. Ask it once when a company is connected, so what is missing is said then and not found out at the first invoice. Answers {ready, checks: [{key, ok, blocking, message, data}]}. Every provider answers company, sales_account (data.accounts: the income accounts a sale can be booked on, {number, name}), bank_account (data.accounts) and accounting_year; a vendor adds its own: Uniconta customer_group and customer_group_sales_account, e-conomic customer_group, vat_zone and payment_terms. ready is false when a blocking check failed. Reads only: nothing is created in the books.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/readiness' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/readiness', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/readiness');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Reports (spend)

GET/v1/unified-accounting/reports?report=expenses&from=2026-09-01&to=2026-09-30&detail=summary

The spend report on any bound company — the same report Dinero's custom reports produce: totals {expenses, salary, expenses_excl_salary, by_type, entries, accounts}, by_type, by_category, by_account (typed, is_salary flagged), by_month, excluded (with reason), unmapped, legend, notes. report = expenses (default) | chart (the whole chart classified, no entries — read it once per company first). from/to default to month to date; detail summary|detailed; group_by=month. One-off overrides: include, exclude, salary (account ranges), include_types, account_types, category_types, keywords. Saved per-binding definitions are not here yet.

Minimal query: {"report":"expenses","from":"2026-09-01","to":"2026-09-30","detail":"summary"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
reportexpenses
from2026-09-01
to2026-09-30
detailsummary
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/reports?report=expenses&from=2026-09-01&to=2026-09-30&detail=summary' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/reports?report=expenses&from=2026-09-01&to=2026-09-30&detail=summary', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/reports?report=expenses&from=2026-09-01&to=2026-09-30&detail=summary');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — Suppliers

GET/v1/unified-accounting/suppliers/:id

Get one supplier by {id}.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/suppliers/:id' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/suppliers/:id', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/suppliers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
GET/v1/unified-accounting/suppliers?page=0&page_size=100

List suppliers in the same canonical contact shape as customers: {suppliers: [...], page, has_more}, with the same exact lookups (email, phone, vat_number).

Minimal query: {"page":0,"page_size":100}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
page0
page_size100
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/suppliers?page=0&page_size=100' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/suppliers?page=0&page_size=100', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/suppliers?page=0&page_size=100');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
POST/v1/unified-accounting/suppliers

Create a supplier. name required.

Minimal body: {"name":"Hosting Co","email":"invoices@hosting.example","address":{"country":"DK"}}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "name": "Hosting Co",
    "email": "invoices@hosting.example",
    "address": {
        "country": "DK"
    }
}
curl -X POST 'https://api.endpointr.com/v1/unified-accounting/suppliers' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "name": "Hosting Co",
    "email": "invoices@hosting.example",
    "address": {
        "country": "DK"
    }
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/suppliers', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "name": "Hosting Co",
      "email": "invoices@hosting.example",
      "address": {
          "country": "DK"
      }
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/suppliers');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"name\": \"Hosting Co\",\n    \"email\": \"invoices@hosting.example\",\n    \"address\": {\n        \"country\": \"DK\"\n    }\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
PUT/v1/unified-accounting/suppliers/:id

Update a supplier by {id} (partial).

Minimal body: {"phone":"+4512345678"}

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "phone": "+4512345678"
}
curl -X PUT 'https://api.endpointr.com/v1/unified-accounting/suppliers/:id' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "phone": "+4512345678"
}'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/suppliers/:id', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "phone": "+4512345678"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/suppliers/:id');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"phone\": \"+4512345678\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Unified accounting — VAT types

GET/v1/unified-accounting/vat-types

The bound company's VAT types as {id, name, rate, kind sales|purchase|none, raw}; id is what invoice and journal lines take as vat_type_id.

_Requires stored credentials: unified-accounting (PUT /v1/credentials/unified-accounting)._

AuthorizationBearer YOUR_JWT_TOKEN
curl -X GET 'https://api.endpointr.com/v1/unified-accounting/vat-types' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN'
const response = await fetch('https://api.endpointr.com/v1/unified-accounting/vat-types', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/unified-accounting/vat-types');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Verifications

Verify (bulk)

POST/v1/verifications/bulk

Verify many values in one call. Body: {values: [...], kind?, provider?}. Same field semantics as POST /v1/verifications/verify; kind and provider apply to every entry.

Max 1000 values per call. The endpoint iterates the provider's single-call API per value — there is no provider-native batching yet, so 1000 emails ≈ 1000 upstream calls.

Response. {count, results: [...]}. Each result has the same shape as the single endpoint plus index (the position in the input array). Per-row error is set when one value couldn't be verified (e.g. empty string, kind mismatch, provider threw) — the rest of the batch still runs.

Required body: values.

Minimal body: {"values":["jane@acme.com","john@example.com"]}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "values": [
        "jane@acme.com",
        "john@example.com"
    ],
    "provider": "reoon"
}
curl -X POST 'https://api.endpointr.com/v1/verifications/bulk' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "values": [
        "jane@acme.com",
        "john@example.com"
    ],
    "provider": "reoon"
}'
const response = await fetch('https://api.endpointr.com/v1/verifications/bulk', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "values": [
          "jane@acme.com",
          "john@example.com"
      ],
      "provider": "reoon"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/verifications/bulk');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"values\": [\n        \"jane@acme.com\",\n        \"john@example.com\"\n    ],\n    \"provider\": \"reoon\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

Verify (single)

POST/v1/verifications/verify

Verify a single email or phone number. Body: {value, kind?, provider?}.

value — the address / number to verify. Required.
kind — email | phone. Optional; auto-detected from value (presence of @ ⇒ email).
provider — slug of a configured verifier (e.g. reoon, numverify, neverbounce). Optional.

Provider selection.
- If provider is set: that exact slug is used. 404 when the customer has no stored credentials for it (or it isn't a known verifier).
- If provider is omitted: the customer's verification_priority list (set under /admin/customers/{id} → Verification priority) is walked, and the first slug whose provider supports the kind wins. 404 when no slug in the list supports the requested kind.

Response. {provider, kind, value, status, raw, http_status, endpoint, request_at}. status is the provider's normalised verdict (safe / valid / invalid / risky / disposable / …). raw is the provider's full response body — refer to the provider's own docs for the fields beyond status.

Status codes. 201 ok · 400 bad request / provider/kind mismatch · 401 invalid token · 404 provider not configured / unknown slug / no priority match · 502 provider returned an empty envelope.

Required body: value.

Minimal body: {"value":"jane@acme.com"}

Content-Typeapplication/json
AuthorizationBearer YOUR_JWT_TOKEN
{
    "value": "jane@acme.com",
    "provider": "reoon"
}
curl -X POST 'https://api.endpointr.com/v1/verifications/verify' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -d '{
    "value": "jane@acme.com",
    "provider": "reoon"
}'
const response = await fetch('https://api.endpointr.com/v1/verifications/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  },
  body: JSON.stringify({
      "value": "jane@acme.com",
      "provider": "reoon"
  })
});
const data = await response.json();
$ch = curl_init('https://api.endpointr.com/v1/verifications/verify');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Authorization: Bearer YOUR_JWT_TOKEN',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, "{\n    \"value\": \"jane@acme.com\",\n    \"provider\": \"reoon\"\n}");
$response = json_decode(curl_exec($ch), true);
curl_close($ch);