1House Global API Documentation

Indicators

Indicator (scanner) catalogue, per-member access, and backoffice grant/revoke

Manage the indicators (scanners) 1House offers, and who holds access to them on TradingView.

Two halves, kept apart

The catalogue is what can be granted — one row per indicator, carrying the TradingView pineId that automation is keyed on. Access is who actually holds each one. Deleting an indicator is a catalogue decision, never a mass revocation, which is why the two are separate.

Overview

The Indicators API provides:

  • Indicator catalogue CRUD (admin)
  • Per-indicator access rosters, grants and revocations (admin)
  • A worklist of grants that need a human
  • Backoffice grant/revoke of a customer's entire indicator set
  • The marketplace request funnel that feeds all of it

Base Path: /v1/streams/scanners

Marketplace: /v1/streams/marketplace

Backoffice webhooks: /v1/streams/webhooks

Indicators and scanners are the same thing

The product calls them indicators; the API paths and payload fields call them scanners. They refer to the same records — no translation is needed beyond the path.

Access lifecycle

Every grant is in exactly one state. Nothing moves between them except through the endpoints below or the automated sweep.

StatusMeaning
pending_grantQueued; the TradingView call has not succeeded yet
activeThe member holds the indicator on TradingView
pending_revokeQueued for removal, or counting down a grace period
revokedRemoved on TradingView
failedRetries are spent — waiting for a human

source decides what automation is allowed to touch a grant:

SourceBehaviour
manualA comped or staff grant. Never touched by the automated revoke sweep
automatedCreated by the backoffice. Follows the member's activity status
marketplaceCame out of an approved marketplace access request

A grant needs a TradingView username

TradingView is the system of record for who can open an invite-only script, and it knows people only by their TradingView handle. A grant without one cannot be pushed, so every grant endpoint either takes a tradingviewUsername or resolves one from an existing grant or marketplace request.

Backoffice: Grant or Revoke All Indicators

Grant or revoke every active indicator for one customer, identified by the backoffice's own customer_id.

POST /v1/streams/webhooks/indicator-access

This endpoint does not use an API key

Like the activity-status webhook, this is a server-to-server call from the external backoffice. It authenticates with the X-Webhook-Secret header alone — no X-API-Key, no JWT — and it is refused outright (503) when no secret is configured on the service.

Headers:

HeaderRequiredDescription
X-Webhook-SecretYesShared secret (WEBHOOK_SECRET), same value the activity-status webhook uses
Content-TypeYesapplication/json

Request Body:

{
  "customer_id": "CUS-10482",
  "action": "grant",
  "tradingview_username": "trader_jane"
}

Request Body Parameters:

ParameterTypeRequiredDescription
customer_idstringYesThe backoffice's customer id. Matched against users.customerId. customerId is also accepted
actionstringYesgrant or revoke. Anything else is a 400 — the value is never guessed at
tradingview_usernamestringOnly when granting and none is on recordThe member's TradingView handle. tradingviewUsername is also accepted

Grant is refused without a TradingView handle

When granting, the service looks for a handle in three places, in order: the request body, any grant this person already holds, then their most recent marketplace access request. If none of them has one, the request is refused with 422 TRADINGVIEW_USERNAME_REQUIRED and nothing is written — rather than creating a row per indicator that is guaranteed to fail. Retry with tradingview_username once you have it.

Behaviour:

  • Grant walks the active catalogue and grants each indicator with source: "automated", which leaves the grants inside the reach of the activity-status sweep. A grant the member already holds is refreshed, not duplicated.
  • Revoke walks the rows the member actually holds rather than the catalogue, so an indicator that has since been retired is still taken away. Revocation is immediate — there is no grace period here, unlike a lapsed subscription.
  • Both are idempotent. Re-sending the same call is always safe.

Try it out:

Example Request:

curl -X POST "https://api-gateway.dev.1houseglobalservices.com/v1/streams/webhooks/indicator-access" \
  -H "X-Webhook-Secret: your-webhook-secret" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "CUS-10482",
    "action": "grant",
    "tradingview_username": "trader_jane"
  }'
const response = await fetch(
  'https://api-gateway.dev.1houseglobalservices.com/v1/streams/webhooks/indicator-access',
  {
    method: 'POST',
    headers: {
      'X-Webhook-Secret': process.env.STREAM_WEBHOOK_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      customer_id: 'CUS-10482',
      action: 'grant',
      tradingview_username: 'trader_jane',
    }),
  }
);

const data = await response.json();
import os
import requests

url = "https://api-gateway.dev.1houseglobalservices.com/v1/streams/webhooks/indicator-access"
headers = {
    "X-Webhook-Secret": os.environ["STREAM_WEBHOOK_SECRET"],
    "Content-Type": "application/json",
}
payload = {
    "customer_id": "CUS-10482",
    "action": "grant",
    "tradingview_username": "trader_jane",
}

response = requests.post(url, headers=headers, json=payload)
result = response.json()

Example Response:

{
  "success": true,
  "message": "Indicator access granted",
  "data": {
    "customerId": "CUS-10482",
    "action": "grant",
    "userId": "68f2c1a4e9b21c0012aa77d1",
    "email": "jane@example.com",
    "tradingviewUsername": "trader_jane",
    "scanners": 5,
    "changed": 5,
    "skipped": 0,
    "failed": 0,
    "results": [
      {
        "scannerId": "68a1f0c2d3b45e0011bc2201",
        "scannerName": "ZEUS Precision Levels",
        "accessId": "68f3aa19d3b45e0011bc9911",
        "status": "active"
      }
    ]
  }
}

Response Fields:

FieldTypeDescription
scannersnumberIndicators in the active catalogue
changednumberGrants or revocations that went through
skippednumberCatalogue entries the member did not hold (revoke only)
failednumberRows that exhausted their retries; they appear on the failures worklist
resultsarrayPer-indicator outcome, including error when one failed

Error Responses:

StatusCodeMeaning
400INVALID_PAYLOADcustomer_id is missing
400INVALID_ACTIONaction was not grant or revoke
401UNAUTHORIZEDWrong or missing X-Webhook-Secret
404CUSTOMER_NOT_FOUNDNo account is mapped to that customer_id — map it on the account first
422TRADINGVIEW_USERNAME_REQUIREDGranting, and no TradingView handle is on record. Nothing was written
503WEBHOOK_NOT_CONFIGUREDWEBHOOK_SECRET is not set on the service

Backoffice: Activity Status

Drives grant/revoke automatically as a member's subscription comes and goes. Going inactive starts a grace period rather than revoking outright, so a failed card does not immediately cost somebody their indicators. manual grants are never touched.

POST /v1/streams/webhooks/activity-status

Request Body:

{
  "userId": "68f2c1a4e9b21c0012aa77d1",
  "status": "inactive"
}
ParameterTypeDescription
userIdstringThe member's user ObjectId. Either this or email is required
emailstringThe member's email, when the user id is not known
activebooleantrue / false. Use this or status
statusstringOne of active, enabled, inactive, cancelled, canceled, expired, suspended, disabled

Unrecognised status is rejected, never guessed

A word this endpoint does not recognise returns 400 UNKNOWN_STATUS. Defaulting an unreadable value to "inactive" would revoke paid access for real people, so the failure is deliberately loud.

Indicator Catalogue

List Indicators

GET /v1/streams/scanners
ParameterTypeDescription
includeInactivebooleantrue to include retired indicators (admin views only)

Each row carries activeAccessCount — a head count, so a list can say "12 people" without fetching every roster.

Example Response:

{
  "success": true,
  "data": [
    {
      "id": "68a1f0c2d3b45e0011bc2201",
      "name": "ZEUS Precision Levels",
      "description": "Session-based precision levels",
      "pineId": "PUB;7a1f9c2e4b8d",
      "tradingviewUrl": "https://www.tradingview.com/script/7a1f9c2e/",
      "isActive": true,
      "sortOrder": 10,
      "activeAccessCount": 128
    }
  ]
}

Get Indicator

GET /v1/streams/scanners/:id

Create Indicator (Admin)

POST /v1/streams/scanners
{
  "name": "ZEUS Precision Levels",
  "description": "Session-based precision levels",
  "pineId": "PUB;7a1f9c2e4b8d",
  "tradingviewUrl": "https://www.tradingview.com/script/7a1f9c2e/",
  "isActive": true,
  "sortOrder": 10
}

pineId is what makes automation possible

pineId is TradingView's own identifier for the script — the PUB;<hash> form shown in the invite-only script's URL. An indicator without one can be listed, but never granted automatically.

Update Indicator (Admin)

PUT /v1/streams/scanners/:id

Delete Indicator (Admin)

DELETE /v1/streams/scanners/:id

Deleting will not strand people

An indicator anyone still holds, or that a marketplace product still points at, cannot be deleted — the request returns 400 telling you how many. Deactivate it instead (isActive: false), which keeps every access row meaningful and lets an admin revoke deliberately.

Per-Indicator Access (Admin)

List Access

GET /v1/streams/scanners/:id/access
ParameterTypeDescription
statusstringFilter by access status (see the lifecycle table above)
pagenumberPage number (default 1)
limitnumberRows per page (default 50, max 200)

Grant Access

POST /v1/streams/scanners/:id/access
{
  "userId": "68f2c1a4e9b21c0012aa77d1",
  "tradingviewUsername": "trader_jane"
}
ParameterTypeRequiredDescription
userIdstringOne of userId / emailThe member's user ObjectId
emailstringOne of userId / emailThe member's email
tradingviewUsernamestringYesTradingView handle to grant on

Grants made here are source: "manual" — deliberately immune to the automated revoke sweep, which is what makes them usable for comped and staff access.

Revoke Access

DELETE /v1/streams/scanners/:id/access/:accessId

Immediate, with no grace period.

Retry a Failed Job

POST /v1/streams/scanners/:id/access/:accessId/retry

Resumes the job that failed rather than reversing it — a retry of a failed revoke revokes, it does not grant.

Failures Worklist

Every grant needing a human, across every indicator.

GET /v1/streams/scanners/access-failures

Each row carries lastError and attempts, so a stuck grant waits visibly for somebody rather than disappearing.

Marketplace

Where a member asks for an indicator in the first place. An approved request for a scanner product is what creates the grant — see Per-Indicator Access for what happens next.

Base Path: /v1/streams/marketplace

Full reference lives on its own page

Marketplace documents the whole surface — authoring and editing products, the custom request-form builder, product image uploads, and the review queue. What follows is only the part of the funnel that ends in an indicator grant.

List Products

GET /v1/streams/marketplace/products

The public catalogue is active products only, and no parameter widens it — the admin listing is a separate route, GET /v1/streams/marketplace/products/all.

ParameterTypeDescription
productTypestringscanner or misc
FieldTypeDescription
productTypestringscanner products carry an entitlement; misc products do not
scannerIdstring | nullWhich indicator an approval grants. Only ever set on scanner products

Submit an Access Request

POST /v1/streams/marketplace/access-requests

The form is the contract

This endpoint accepts what the 1hstream marketplace form collects — name, email, TradingView handle — plus answers to any custom questions the product defines, and requires the same fields the form marks required. Nothing else is read from the body.

{
  "productId": "68a1f0c2d3b45e0011bc3301",
  "fullName": "Jane Ruiz",
  "email": "jane@example.com",
  "tradingviewId": "trader_jane"
}
ParameterTypeRequiredDescription
productIdstringYesThe product being requested
fullNamestringYesTrimmed before storing
emailstringYesLower-cased before storing; must look like an email address
tradingviewIdstringFor scanner productsThe member's TradingView handle

A scanner request without a handle is refused

tradingviewId is what an approval is eventually delivered with, so a request for a scanner product is rejected with 400 without one. Accepting it would move the failure to the moment an admin clicks Approve, where it reads as the approval breaking rather than the form being incomplete. misc products carry no entitlement and leave the field optional.

Re-submitting updates, it does not duplicate

A second request for the same product and email updates the pending or denied one — that is how somebody corrects a mistyped TradingView handle. A request that has already been approved is returned untouched: its details have been acted on, and rewriting them would desync the grant from the record that produced it.

Try it out:

Legacy fields

Requests taken before the form settled may carry a phone or message. Both are still returned so a reviewer can read them, but neither is collected or accepted any more.

Check Request Status

GET /v1/streams/marketplace/access-requests/status?email=jane@example.com

Returns a { [productId]: status } map, picking approved over anything else when a member has more than one request for the same product.

Review Requests (Admin)

GET /v1/streams/marketplace/access-requests
PATCH /v1/streams/marketplace/access-requests/:id

PATCH takes { "status": "approved" | "denied" | "pending" }. For a scanner product the decision is the entitlement: approving creates or revives the access row and queues the TradingView grant; withdrawing an approval queues the revoke, and only for the row this request created — a separate manual grant to the same person survives.

A decision is never lost to a third party

If the TradingView call cannot be made, the decision is still saved and the response carries an entitlementWarning. The grant lands in failed and appears on the failures worklist. Surface the warning rather than treating the call as a plain success.

  • Marketplace — authoring the products that feed these requests
  • Live Streams — schedules and streaming sessions from the same service
  • Trading — trade ideas, which reference indicators by indicatorId
  • Authentication — mapping an account to a backoffice customerId