Skip to content

Integrations

Integrations connect a workspace to an external provider (e.g. IDPoS, KosmetologAPI) for inbound webhooks, outbound sync, and historical imports. Everything is provider-agnostic and keyed by a :provider slug — the set of valid slugs is decided server-side by the provider registry. Each provider declares which capabilities it supports (inbound, outbound, import); endpoints that depend on a capability the provider lacks return an error.

Auth: Every endpoint is workspace-scoped — pass a Bearer token for a user who is a member of the workspace in the path. Access is enforced by createWorkspaceGuard (path source) plus an explicit checkWorkspaceAccess call in each handler.

All paths are mounted under the /wallethero-api prefix. Base URL: https://api.wallethero.app.

Secrets are never returned. Only redacted secrets_meta (e.g. { "api_key": { "last4": "9f02", "rotated_at": "..." } }) appears in responses.


List Integrations

GET /wallethero-api/workspace/:workspaceId/integrations

Returns every configured integration for the workspace plus the list of provider slugs the server supports.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    {
      "id": "c1...",
      "workspace_id": "WORKSPACE_ID",
      "provider": "idpos",
      "enabled_inbound": true,
      "enabled_outbound": false,
      "inbox_id": "ibx_123",
      "inbox_ingest_url": "https://webhook.wallethero.app/ingest/TOKEN",
      "config": { "base_url": "https://...", "location_ids": [1, 2] },
      "secrets_meta": { "api_key": { "last4": "9f02", "rotated_at": "2026-06-01T10:00:00.000Z" } },
      "last_synced_at": null,
      "last_error": null,
      "date_created": "2026-06-01T10:00:00.000Z",
      "date_updated": "2026-06-01T10:00:00.000Z"
    }
  ],
  "available_providers": ["idpos", "kosmetolog"]
}

SDK

typescript
const { data, available_providers } = await wh.integrations.list(workspaceId);

Get Integration

GET /wallethero-api/workspace/:workspaceId/integrations/:provider

Returns the saved config for one provider. Responds 404 ({ "error": { "code": "not_found" } }) when the provider isn't configured for the workspace.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug (e.g. idpos). Must be a registered provider

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "c1...",
    "provider": "idpos",
    "enabled_inbound": true,
    "enabled_outbound": false,
    "inbox_id": "ibx_123",
    "inbox_ingest_url": "https://webhook.wallethero.app/ingest/TOKEN",
    "config": { "base_url": "https://...", "location_ids": [1, 2] },
    "secrets_meta": { "api_key": { "last4": "9f02" } },
    "last_synced_at": null,
    "last_error": null
  }
}

SDK

typescript
const result = await wh.integrations.get(workspaceId, "idpos");
// result is null when not configured

Create or Update Integration

PUT /wallethero-api/workspace/:workspaceId/integrations/:provider

Creates or updates (upsert) the integration config for a provider. config is shallow-merged and validated against the provider's schema; secrets are validated against the provider's known secret keys. Enabling inbound auto-provisions a webhook inbox (for webhook-receiving providers) and returns the inbox token. Enabling outbound is gated — if the provider's prerequisites (e.g. tier mappings) aren't satisfied, the request fails with the missing reason.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug. Must be a registered provider

Request Body

FieldTypeRequiredDescription
enabled_inboundbooleanNoTurn inbound (webhook receive / import) on or off
enabled_outboundbooleanNoTurn outbound sync on or off (gated by provider prerequisites)
configobjectNoProvider-specific config. Validated against the provider's configSchema. For IDPoS this includes base_url, location_ids, default_location_id, tier_mappings, customer_id_mapping, etc.
secretsRecord<string, string | null>NoPer-key secret rotation. A string sets/rotates the value, null clears it, omitting a key leaves it unchanged. Unknown secret keys are rejected

Example Request

bash
curl -X PUT "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled_inbound": true,
    "config": { "base_url": "https://gateway.idpos.example", "location_ids": [1], "default_location_id": 1 },
    "secrets": { "api_key": "SECRET_VALUE" }
  }'

Response (200)

json
{
  "data": {
    "id": "c1...",
    "provider": "idpos",
    "enabled_inbound": true,
    "inbox_ingest_url": "https://webhook.wallethero.app/ingest/TOKEN",
    "config": { "base_url": "https://gateway.idpos.example", "location_ids": [1] },
    "secrets_meta": { "api_key": { "last4": "alue" } }
  },
  "token": "INBOX_INGEST_TOKEN"
}

token is present only when inbound was just auto-provisioned. A save_warning string is included when the save succeeded but a provider post-save hook (e.g. IDPoS webhook reconcile) failed.

SDK

typescript
const { data, token } = await wh.integrations.upsert(workspaceId, "idpos", {
  enabled_inbound: true,
  config: { base_url: "https://gateway.idpos.example", location_ids: [1], default_location_id: 1 },
  secrets: { api_key: "SECRET_VALUE" },
});

Delete Integration

DELETE /wallethero-api/workspace/:workspaceId/integrations/:provider

Deletes the integration config and best-effort revokes its webhook inbox. Responds 204 with no body.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug

Example Request

bash
curl -X DELETE "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (204)

No content.

SDK

typescript
await wh.integrations.delete(workspaceId, "idpos");

Provision Inbox

POST /wallethero-api/workspace/:workspaceId/integrations/:provider/provision-inbox

Idempotently creates a webhook inbox for inbound events and returns the ingest URL + token. If an inbox already exists upstream the token is not regenerated (token is null) — use rotate-inbox-token to mint a new one. Only valid for providers that receive webhooks.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/provision-inbox" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "c1...",
    "provider": "idpos",
    "inbox_id": "ibx_123",
    "inbox_ingest_url": "https://webhook.wallethero.app/ingest/TOKEN"
  },
  "token": "INBOX_INGEST_TOKEN"
}

SDK

typescript
const { data, token } = await wh.integrations.provisionInbox(workspaceId, "idpos");

Rotate Inbox Token

POST /wallethero-api/workspace/:workspaceId/integrations/:provider/rotate-inbox-token

Rotates the inbound inbox token, invalidating the old ingest URL. Requires an already-provisioned inbox.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/rotate-inbox-token" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "c1...",
    "inbox_ingest_url": "https://webhook.wallethero.app/ingest/NEW_TOKEN"
  },
  "token": "NEW_INBOX_INGEST_TOKEN"
}

SDK

typescript
const { data, token } = await wh.integrations.rotateInboxToken(workspaceId, "idpos");

Integration Health

GET /wallethero-api/workspace/:workspaceId/integrations/:provider/health

Returns the saved config plus live webhook-inbox queue stats (pending/leased/processed/failed counts, events processed in the last 24h, and the most recent failed event). When the recorded inbox no longer exists upstream the stale reference is cleared and inbox_missing: true is returned (the integration self-heals by re-provisioning if inbound is on).

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/health" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "config": { "id": "c1...", "provider": "idpos", "enabled_inbound": true },
    "inbox": {
      "counts": { "pending": 0, "leased": 0, "processed": 124, "failed": 2 },
      "processed_24h": 30,
      "last_failed": {
        "id": "evt_999",
        "received_at": "2026-06-02T08:00:00.000Z",
        "last_error": "signature mismatch",
        "attempts": 3
      }
    }
  }
}

inbox is null when no inbox is provisioned (or stats are temporarily unavailable). data.inbox_missing: true appears when the upstream inbox was lost.

SDK

typescript
const { data } = await wh.integrations.health(workspaceId, "idpos");

Test Connection

POST /wallethero-api/workspace/:workspaceId/integrations/:provider/test-connection

Tests connectivity to the provider. You may pass credentials in the body to validate creds before saving them; omit the body to test the stored credentials. Providers without a tester return { ok: true, note: "no test for this provider" }. For IDPoS a successful test also returns the location catalog so a setup wizard can render a picker.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug

Request Body

FieldTypeRequiredDescription
credentialsobjectNoProvider-specific credentials to test (IDPoS: client_id/api_key; KosmetologAPI: email/password). Omit to test stored credentials

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/test-connection" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "credentials": { "client_id": "...", "api_key": "..." } }'

Response (200)

json
{
  "data": {
    "ok": true,
    "status": 200,
    "locations": [{ "id": 1, "name": "Main store" }]
  }
}

SDK

typescript
const { data } = await wh.integrations.testConnection(workspaceId, "idpos", {
  credentials: { client_id: "...", api_key: "..." },
});

Start Historical Import

POST /wallethero-api/workspace/:workspaceId/integrations/:provider/import-transactions

Kicks off a historical transaction import for a date range. The request is validated by the provider module, a job row is inserted (status: "pending"), and a background worker performs the fetch+ingest. Returns immediately with the job so you can poll for progress. Only available for providers with the import capability.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug (must support import)

Request Body

Provider-specific and validated by the provider module. For IDPoS:

FieldTypeRequiredDescription
date_fromstringYesStart of range
date_tostringYesEnd of range
location_idsnumber[]YesLocations to import (subset of the configured locations)

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/import-transactions" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "date_from": "2026-05-01", "date_to": "2026-05-31", "location_ids": [1] }'

Response (200)

json
{
  "data": {
    "id": "job_123",
    "workspace_id": "WORKSPACE_ID",
    "provider": "idpos",
    "status": "pending",
    "date_from": "2026-05-01",
    "date_to": "2026-05-31",
    "location_ids": [1],
    "fetch_total": 31,
    "fetch_done": 0,
    "processed_count": 0,
    "imported_count": 0,
    "date_created": "2026-06-02T10:00:00.000Z"
  }
}

SDK

typescript
const { data } = await wh.integrations.startImport(workspaceId, "idpos", {
  date_from: "2026-05-01",
  date_to: "2026-05-31",
  location_ids: [1],
});

List Import Jobs

GET /wallethero-api/workspace/:workspaceId/integrations/:provider/import-jobs

Lists recent import jobs for the provider, most recent first. Only available for import-capable providers.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug (must support import)

Query Parameters

ParameterTypeRequiredDefaultDescription
limitnumberNo20Max jobs to return

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/import-jobs?limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    {
      "id": "job_123",
      "status": "running",
      "phase": "fetching",
      "fetch_total": 31,
      "fetch_done": 12,
      "imported_count": 340,
      "date_created": "2026-06-02T10:00:00.000Z"
    }
  ]
}

SDK

typescript
const { data } = await wh.integrations.listImportJobs(workspaceId, "idpos", 20);

Get Import Job

GET /wallethero-api/workspace/:workspaceId/integrations/:provider/import-jobs/:jobId

Fetches a single job's status, counters, and error log. Poll this while a job is pending/running. Responds 404 when the job doesn't exist.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug (must support import)
jobIdstring (UUID)Import job identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/import-jobs/JOB_ID" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "JOB_ID",
    "status": "completed",
    "phase": null,
    "fetch_total": 31,
    "fetch_done": 31,
    "total_fetched": 1200,
    "processed_count": 1200,
    "imported_count": 1180,
    "skipped_count": 20,
    "failed_count": 0,
    "error_log": [],
    "last_error": null,
    "completed_at": "2026-06-02T10:20:00.000Z"
  }
}

SDK

typescript
const result = await wh.integrations.getImportJob(workspaceId, "idpos", jobId);
// result is null when the job is not found

Cancel Import Job

POST /wallethero-api/workspace/:workspaceId/integrations/:provider/import-jobs/:jobId/cancel

Requests cancellation. A pending job is cancelled immediately; a running job's cancel_requested flag is set and the worker stops after the in-flight row, preserving partial counters. Responds 404 when the job doesn't exist.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier
providerstringProvider slug (must support import)
jobIdstring (UUID)Import job identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/import-jobs/JOB_ID/cancel" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "id": "JOB_ID",
    "status": "cancelled",
    "cancel_requested": true
  }
}

SDK

typescript
const { data } = await wh.integrations.cancelImportJob(workspaceId, "idpos", jobId);

Provider-specific endpoints

Beyond the generic surface above, each provider module mounts its own routes under /wallethero-api/workspace/:workspaceId/integrations/<slug>/…. For IDPoS these include the location/discount/customer-group catalogs, customer-id backfill, and upstream webhook register/unregister/status, exposed via dedicated SDK methods (wh.integrations.listIdposLocations, listIdposDiscounts, listIdposCustomerGroups, backfillIdposCustomerIds, getIdposWebhookStatus, registerIdposWebhook, unregisterIdposWebhook). See the IDPoS provider reference for details.


IDPoS Provider Endpoints

These routes are mounted per-provider under the idpos slug (prefix /wallethero-api/workspace/:workspaceId/integrations/idpos) and require the IDPoS integration to be configured for the workspace. All run behind the workspace-member guard.

Backfill Customer IDs

POST /wallethero-api/workspace/:workspaceId/integrations/idpos/backfill-customer-ids

Stamps a random N-digit ID onto the configured client custom field for every client in the workspace whose field is currently empty. Only valid when the IDPoS customer_id_mapping is set to custom_field with auto_generate.enabled = true; otherwise responds 400. Iterates clients in batches and reports per-client failures.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/backfill-customer-ids" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json"

Response (200)

json
{
  "data": {
    "processed": 120,
    "generated": 118,
    "skipped_already_set": 0,
    "errors": [
      { "client_id": "5d2b…", "error": "Failed to generate unique id" }
    ]
  }
}

SDK

typescript
const { data } = await wh.integrations.backfillIdposCustomerIds(workspaceId);

List Locations

GET /wallethero-api/workspace/:workspaceId/integrations/idpos/locations

Fetches the live IDPoS Location catalog so the config page can resolve names for the saved location_ids and offer additions without re-running a connection test.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/locations" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    { "id": 1, "name": "Main Store" },
    { "id": 2, "name": "Airport Kiosk" }
  ]
}

SDK

typescript
const { data } = await wh.integrations.listIdposLocations(workspaceId);

List Discounts

GET /wallethero-api/workspace/:workspaceId/integrations/idpos/discounts

Fetches the live IDPoS discount catalog, used to render the discount IDs that live on each CustomerGroup so operators can pick the right group per tier.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/discounts" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    {
      "id": 10,
      "name": "VIP 10%",
      "value": 10,
      "discountType": "Percentage",
      "isActive": true
    }
  ]
}

SDK

typescript
const { data } = await wh.integrations.listIdposDiscounts(workspaceId);

List Customer Groups

GET /wallethero-api/workspace/:workspaceId/integrations/idpos/customer-groups

Fetches the live IDPoS CustomerGroup catalog so operators can map each WalletHero tier to a group. The group's discounts array is what drives the customer's rebates in IDPoS after a tier change.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/customer-groups" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": [
    { "id": 7, "name": "Gold", "discounts": [10, 11] }
  ]
}

SDK

typescript
const { data } = await wh.integrations.listIdposCustomerGroups(workspaceId);

Webhook Status

GET /wallethero-api/workspace/:workspaceId/integrations/idpos/webhook/status

Reports per-location webhook registration status. For each entry in location_ids it indicates whether a local webhook id is stored and whether it's still alive upstream; stale entries (id stored but IDPoS returned 404) are pruned from the stored map. registered is true only when every configured location has a verified upstream webhook.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/webhook/status" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response (200)

json
{
  "data": {
    "registered": true,
    "has_any": true,
    "locations": [
      { "location_id": 1, "webhook_id": 42, "registered": true, "stale": false },
      { "location_id": 2, "webhook_id": null, "registered": false, "stale": false }
    ]
  }
}

SDK

typescript
const { data } = await wh.integrations.getIdposWebhookStatus(workspaceId);

Register Webhooks

POST /wallethero-api/workspace/:workspaceId/integrations/idpos/webhook/register

Reconciles per-location webhook registrations in IDPoS to match location_ids. Idempotent — re-running after a config change creates webhooks for new locations and removes them for unselected ones. Requires inbound to be provisioned first. Persists the resulting id map and returns it.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/webhook/register" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json"

Response (200)

json
{
  "data": {
    "registered": true,
    "idpos_webhook_ids": { "1": 42, "2": 43 }
  }
}

SDK

typescript
const { data } = await wh.integrations.registerIdposWebhook(workspaceId);

Unregister Webhooks

POST /wallethero-api/workspace/:workspaceId/integrations/idpos/webhook/unregister

Removes all per-location webhooks from IDPoS and clears the locally-stored id map. Idempotent — missing upstream entries (404) are tolerated.

Auth: Bearer token — workspace member.

Path Parameters

ParameterTypeDescription
workspaceIdstring (UUID)Workspace identifier

Example Request

bash
curl -X POST "https://api.wallethero.app/wallethero-api/workspace/WORKSPACE_ID/integrations/idpos/webhook/unregister" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json"

Response (200)

json
{
  "data": { "registered": false }
}

SDK

typescript
const { data } = await wh.integrations.unregisterIdposWebhook(workspaceId);

WalletHero Documentation