curl --request GET \
--url https://app.reputably.net/api/entities/BusinessLocation/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.reputably.net/api/entities/BusinessLocation/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.reputably.net/api/entities/BusinessLocation/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.reputably.net/api/entities/BusinessLocation/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.reputably.net/api/entities/BusinessLocation/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.reputably.net/api/entities/BusinessLocation/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.reputably.net/api/entities/BusinessLocation/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"google_place_id": "<string>",
"google_account_id": "<string>",
"google_location_id": "<string>",
"facebook_page_id": "<string>",
"youtube_channel_id": "<string>",
"reddit_account_id": "<string>",
"reddit_username": "<string>",
"source": "google",
"added_via": "gbp_oauth",
"business_name": "<string>",
"business_category": "<string>",
"address_line1": "<string>",
"address_line2": "<string>",
"city": "<string>",
"state": "<string>",
"postal_code": "<string>",
"country": "<string>",
"phone": "<string>",
"website": "<string>",
"google_maps_url": "<string>",
"latitude": 123,
"longitude": 123,
"cover_photo_url": "<string>",
"logo_url": "<string>",
"average_rating": 123,
"total_reviews": 123,
"is_verified": true,
"is_active": true,
"is_prospect": true,
"billing_exempt": true,
"trial_state": "trialing",
"trial_started_at": "<string>",
"trial_end": "<string>",
"trial_converted_at": "<string>",
"trial_ended_at": "<string>",
"trial_charge_amount": 123,
"trial_charge_interval": "month",
"last_synced_at": "<string>",
"last_full_review_sync_at": "<string>",
"review_count_recheck_after": "<string>",
"review_link": "<string>",
"ai_insights": {},
"ai_insights_updated_at": "<string>",
"ai_insights_review_count": 123,
"is_online_only": true,
"is_online_only_overridden": true,
"enrichment": {},
"enrichment_overrides": {},
"manual_notes": "<string>",
"enrichment_updated_at": "<string>",
"enrichment_source": "first-add",
"enrichment_status": "pending",
"enrichment_error": "<string>",
"auto_response_enabled": true
}Get one business
Reads a single business by id. Ids do not cross workspaces.
curl --request GET \
--url https://app.reputably.net/api/entities/BusinessLocation/{id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.reputably.net/api/entities/BusinessLocation/{id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.reputably.net/api/entities/BusinessLocation/{id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.reputably.net/api/entities/BusinessLocation/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.reputably.net/api/entities/BusinessLocation/{id}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.reputably.net/api/entities/BusinessLocation/{id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.reputably.net/api/entities/BusinessLocation/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"google_place_id": "<string>",
"google_account_id": "<string>",
"google_location_id": "<string>",
"facebook_page_id": "<string>",
"youtube_channel_id": "<string>",
"reddit_account_id": "<string>",
"reddit_username": "<string>",
"source": "google",
"added_via": "gbp_oauth",
"business_name": "<string>",
"business_category": "<string>",
"address_line1": "<string>",
"address_line2": "<string>",
"city": "<string>",
"state": "<string>",
"postal_code": "<string>",
"country": "<string>",
"phone": "<string>",
"website": "<string>",
"google_maps_url": "<string>",
"latitude": 123,
"longitude": 123,
"cover_photo_url": "<string>",
"logo_url": "<string>",
"average_rating": 123,
"total_reviews": 123,
"is_verified": true,
"is_active": true,
"is_prospect": true,
"billing_exempt": true,
"trial_state": "trialing",
"trial_started_at": "<string>",
"trial_end": "<string>",
"trial_converted_at": "<string>",
"trial_ended_at": "<string>",
"trial_charge_amount": 123,
"trial_charge_interval": "month",
"last_synced_at": "<string>",
"last_full_review_sync_at": "<string>",
"review_count_recheck_after": "<string>",
"review_link": "<string>",
"ai_insights": {},
"ai_insights_updated_at": "<string>",
"ai_insights_review_count": 123,
"is_online_only": true,
"is_online_only_overridden": true,
"enrichment": {},
"enrichment_overrides": {},
"manual_notes": "<string>",
"enrichment_updated_at": "<string>",
"enrichment_source": "first-add",
"enrichment_status": "pending",
"enrichment_error": "<string>",
"auto_response_enabled": true
}Authorizations
An agent key (rpk_...) created in Settings → API & MCP, sent as Authorization: Bearer rpk_.... Bearer only: a cookie session can never drive this API. An OAuth 2.1 access token obtained from the same host works identically and lands on the same ceiling.
Headers
Which workspace to read. Omit it and you get the account’s home workspace, which on an agency account is often not where the live businesses are. A workspace this credential cannot read is refused with 403 rather than quietly answered from the default.
Path Parameters
The business id.
Response
The business.
Unique id.
Set when this BusinessLocation was created from a Facebook Page connected in Settings. Mutually exclusive with google_location_id in practice — a chain's GBP listing and its Facebook Page get separate BusinessLocation rows.
External YouTube channel id (UC…) bound to this location. Set from the Connections panel on BusinessDetail. The reply resolver joins this against OAuthIntegration.provider_account_id to pick which connected channel posts the comment.
Reddit account fullname (t2_…) bound to this location. Same role as youtube_channel_id — the reply resolver joins this against OAuthIntegration.provider_account_id to pick which connected Reddit account authors the comment.
Denormalised display name for the bound Reddit account. The stable identifier is reddit_account_id (t2_…); this is just for UI affordances since usernames can change.
google, facebook How this business was added, which decides how its reviews are read.
gbp_oauth, places, website, dfs_keyword Created by generateProspectAudit for a business the agency is pitching, not a client. Always paired with is_active=false. Filtered out of every business picker/list via useBusinessLocations' default exclusion; surfaces only on the Prospect Audits page. Conversion path: clear the flag (or just activate the location) when the prospect signs.
Comp/internal location — excluded from plan capacity math and over-capacity checks.
Per-location trial discriminator for agency-onboarded clients added BEYOND paid capacity. 'trialing' = free now, excluded from capacity, will auto-convert at trial_end; 'converted' = trial billed and now counts as a normal paid location; 'ended' = trial canceled/expired without converting (the row is deactivated but kept, which blocks a repeat trial). Distinct from billing_exempt (comp = forever-free, never counts). Absent on rows that never trialed.
trialing, converted, ended Set once when a trial is first started. Its presence (keyed on stable location identity) is the anti-abuse signal — a location that has ever trialed can't trial again.
Conversion deadline. The trialScheduler converts the location at/after this instant.
Set when a trial ends without converting (scheduler expiry or user cancel-during-trial). Feeds the 'Free trial ended on ' note on the business card and its trial_ended notification. Absent on rows ended before this field existed — falls back to trial_end for display.
Add-on rate captured at trial start, for display only ('converts to $X/mo on '). Conversion always recomputes the real charge from live Stripe.
month, year When the GBP v4 reviews endpoint was last paged to completion for this location. The scheduler's blanket sync is incremental (1 call/location/tick); this drives the daily forced full sweep, which is the only thing that catches an owner reply typed into Google's own UI — a reply does not move the review's updateTime, so it never floats back to page 1.
Set when a full re-page failed to resolve a totalReviewCount vs stored-row-count disagreement — i.e. Google counts reviews its own list endpoint won't return. Suppresses the reconciliation guard until this time so a permanently-off-by-N location can't force a full page-through on every 15-min tick. Cleared by any successful reconciliation or manual Sync.
True for businesses with no physical location. Auto-inferred during enrichment from address fields + business_category. Manually overridable from /businesses/:id (PR-23). Online-only businesses always search globally; a tracker's explicit geo_scope still applies analysis-time filtering when the profile has country/state/city or serves_areas on file.
True if a user manually set is_online_only. Enrichment will not overwrite the value when this is true.
Structured business profile derived from website scrape + extended GBP fetch + LLM extraction. Populated by businessEnrichment.js. Empty until enrichment runs successfully.
Per-field user edits that shadow the auto-generated enrichment. Stored separately from enrichment so a manual refresh doesn't clobber them. When an override key is present, getEffectiveEnrichment uses it in place of the enrichment value. Keys: about (string), services (string[]), attributes (string[]).
Free-form context the user wants the AI to know about this business. Appended to the mention-analysis prompt alongside the enrichment summary. No auto-generated counterpart — pure user input.
first-add, manual-refresh 'partial' = website scrape failed but GBP succeeded (or vice versa); the profile is still useful.
pending, success, partial, failed Per-business opt-in for the AI review-reply worker. When the agency has configured rules in auto_response_settings, only locations with this flag set to true will get auto-replies queued. Default false — owners must opt in each business explicitly so a chain isn't bulk-enrolled.