synup-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -0
- package/dist/index.cjs +1405 -0
- package/dist/index.d.cts +911 -0
- package/dist/index.d.ts +911 -0
- package/dist/index.js +1376 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# synup-js
|
|
2
|
+
|
|
3
|
+
Node.js/TypeScript SDK for the [Synup](https://synup.com) v4 API. Manage locations, listings, reviews, analytics, and more across 50+ directories.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install synup-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SynupClient } from 'synup-js';
|
|
15
|
+
|
|
16
|
+
const client = new SynupClient({
|
|
17
|
+
apiKey: 'YOUR_API_KEY', // Settings -> Integrations -> Generate
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Fetch first 10 locations
|
|
21
|
+
const result = await client.fetchAllLocations({ first: 10 });
|
|
22
|
+
console.log(result.locations);
|
|
23
|
+
|
|
24
|
+
// Fetch all locations (auto-paginate)
|
|
25
|
+
const allLocations = await client.fetchAllLocations({ fetchAll: true });
|
|
26
|
+
|
|
27
|
+
// Get reviews for a location
|
|
28
|
+
const reviews = await client.fetchInteractions(16808, {
|
|
29
|
+
startDate: '2024-01-01',
|
|
30
|
+
endDate: '2024-12-31',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Respond to a review
|
|
34
|
+
await client.respondToReview('interaction-id', 'Thank you for the feedback!');
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Node.js 18+
|
|
40
|
+
- Synup API key
|
|
41
|
+
|
|
42
|
+
## Method Reference
|
|
43
|
+
|
|
44
|
+
### Account
|
|
45
|
+
|
|
46
|
+
| Method | Description |
|
|
47
|
+
|--------|-------------|
|
|
48
|
+
| `fetchPlanSites()` | Get plan site allocations |
|
|
49
|
+
| `fetchCountries()` | List supported countries |
|
|
50
|
+
| `fetchSubscriptions()` | Get subscription details |
|
|
51
|
+
| `createTemporaryCloseAutomation(opts)` | Schedule a temporary location closure |
|
|
52
|
+
|
|
53
|
+
### Locations
|
|
54
|
+
|
|
55
|
+
| Method | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `fetchAllLocations(opts)` | List locations (paginated or fetchAll) |
|
|
58
|
+
| `fetchLocationsByIds(ids)` | Fetch specific locations by ID |
|
|
59
|
+
| `fetchLocationsByStoreCodes(codes)` | Fetch locations by store code |
|
|
60
|
+
| `fetchLocationsByTags(tags, opts)` | Fetch locations filtered by tags |
|
|
61
|
+
| `fetchLocationsByFolder(opts)` | Fetch locations in a folder |
|
|
62
|
+
| `searchLocations(query, opts)` | Search locations by name/address |
|
|
63
|
+
| `createLocation(data)` | Create a new location |
|
|
64
|
+
| `updateLocation(data)` | Update an existing location |
|
|
65
|
+
| `archiveLocations(ids)` | Archive locations |
|
|
66
|
+
| `activateLocations(ids)` | Reactivate archived locations |
|
|
67
|
+
| `cancelArchiveLocations(ids, scope, reason)` | Cancel a pending archive |
|
|
68
|
+
|
|
69
|
+
### Tags
|
|
70
|
+
|
|
71
|
+
| Method | Description |
|
|
72
|
+
|--------|-------------|
|
|
73
|
+
| `fetchTags()` | List all tags |
|
|
74
|
+
| `addLocationTag(locationId, tag)` | Add a tag to a location |
|
|
75
|
+
| `removeLocationTag(locationId, tag)` | Remove a tag from a location |
|
|
76
|
+
|
|
77
|
+
### Folders
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|--------|-------------|
|
|
81
|
+
| `fetchFoldersFlat()` | List all folders (flat list) |
|
|
82
|
+
| `fetchFoldersTree()` | List folders as a nested tree |
|
|
83
|
+
| `fetchFolderDetails(opts)` | Get folder details by name or ID |
|
|
84
|
+
| `createFolder(name, opts)` | Create a new folder |
|
|
85
|
+
| `renameFolder(oldName, newName)` | Rename a folder |
|
|
86
|
+
| `deleteFolder(name)` | Delete a folder |
|
|
87
|
+
| `addLocationsToFolder(folderName, locationIds)` | Add locations to a folder |
|
|
88
|
+
| `removeLocationsFromFolder(locationIds)` | Remove locations from their folder |
|
|
89
|
+
|
|
90
|
+
### Listings
|
|
91
|
+
|
|
92
|
+
| Method | Description |
|
|
93
|
+
|--------|-------------|
|
|
94
|
+
| `fetchPremiumListings(locationId)` | Get premium directory listings |
|
|
95
|
+
| `fetchVoiceListings(locationId)` | Get voice search listings |
|
|
96
|
+
| `fetchAdditionalListings(locationId)` | Get additional directory listings |
|
|
97
|
+
| `fetchDuplicateListings(locationId)` | Get duplicate listings for a location |
|
|
98
|
+
| `fetchAllDuplicateListings(opts)` | Get all duplicate listings across locations |
|
|
99
|
+
| `fetchAiListings(locationId)` | Get AI-powered listing suggestions |
|
|
100
|
+
| `markListingsAsDuplicate(locationId, ids)` | Mark listings as duplicates |
|
|
101
|
+
| `markListingsAsNotDuplicate(locationId, ids)` | Mark listings as not duplicates |
|
|
102
|
+
|
|
103
|
+
### Reviews
|
|
104
|
+
|
|
105
|
+
| Method | Description |
|
|
106
|
+
|--------|-------------|
|
|
107
|
+
| `fetchInteractions(locationId, opts)` | Fetch reviews/interactions |
|
|
108
|
+
| `fetchReviewDetails(interactionIds)` | Get detailed review data |
|
|
109
|
+
| `fetchReviewSettings(locationId)` | Get review notification settings |
|
|
110
|
+
| `editReviewSettings(locationId, settings)` | Update review notification settings |
|
|
111
|
+
| `fetchReviewSiteConfig()` | Get review site configuration |
|
|
112
|
+
| `respondToReview(interactionId, response)` | Respond to a review |
|
|
113
|
+
| `editReviewResponse(reviewId, responseId, text)` | Edit an existing response |
|
|
114
|
+
| `archiveReviewResponse(responseId)` | Archive a review response |
|
|
115
|
+
| `fetchReviewAnalyticsOverview(locationId, opts)` | Review analytics summary |
|
|
116
|
+
| `fetchReviewAnalyticsTimeline(locationId, opts)` | Review analytics over time |
|
|
117
|
+
| `fetchReviewAnalyticsSitesStats(locationId, opts)` | Review analytics by site |
|
|
118
|
+
| `fetchReviewPhrases(opts)` | Phrase/sentiment analysis on reviews |
|
|
119
|
+
|
|
120
|
+
### Keywords
|
|
121
|
+
|
|
122
|
+
| Method | Description |
|
|
123
|
+
|--------|-------------|
|
|
124
|
+
| `fetchKeywords(locationId)` | List tracked keywords |
|
|
125
|
+
| `addKeywords(locationId, keywords)` | Add keywords to track |
|
|
126
|
+
| `archiveKeyword(keywordId)` | Archive a tracked keyword |
|
|
127
|
+
| `fetchKeywordsPerformance(locationId, opts)` | Keyword ranking performance |
|
|
128
|
+
| `fetchRankingAnalyticsTimeline(opts)` | Ranking trends over time |
|
|
129
|
+
| `fetchRankingSitewiseHistogram(opts)` | Ranking distribution by site |
|
|
130
|
+
|
|
131
|
+
### Analytics
|
|
132
|
+
|
|
133
|
+
| Method | Description |
|
|
134
|
+
|--------|-------------|
|
|
135
|
+
| `fetchGoogleAnalytics(locationId, opts)` | Google Business Profile insights |
|
|
136
|
+
| `fetchBingAnalytics(locationId, opts)` | Bing Places insights |
|
|
137
|
+
| `fetchFacebookAnalytics(locationId, opts)` | Facebook page insights |
|
|
138
|
+
|
|
139
|
+
### Photos
|
|
140
|
+
|
|
141
|
+
| Method | Description |
|
|
142
|
+
|--------|-------------|
|
|
143
|
+
| `fetchLocationPhotos(locationId)` | List photos for a location |
|
|
144
|
+
| `fetchPhotoUploadStatus(requestId)` | Check photo upload status |
|
|
145
|
+
| `addLocationPhotos(locationId, photos)` | Upload photos to a location |
|
|
146
|
+
| `removeLocationPhotos(locationId, photoIds)` | Remove photos |
|
|
147
|
+
| `starLocationPhotos(locationId, mediaIds, star)` | Star/unstar photos |
|
|
148
|
+
|
|
149
|
+
### Connections
|
|
150
|
+
|
|
151
|
+
| Method | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `fetchConnectionInfo(locationId)` | Get connection status for a location |
|
|
154
|
+
| `fetchConnectedAccounts(opts)` | List connected accounts |
|
|
155
|
+
| `fetchConnectedAccountDetails(accountId)` | Get details for a connected account |
|
|
156
|
+
| `fetchConnectedAccountFolders(accountId)` | Get folders for a connected account |
|
|
157
|
+
| `fetchConnectedAccountListings(accountId, opts)` | Get listings for a connected account |
|
|
158
|
+
| `fetchConnectionSuggestions(accountId, opts)` | Get matching suggestions |
|
|
159
|
+
| `getOauthConnectUrl(locationId, source, successUrl, errorUrl)` | Get OAuth connect URL |
|
|
160
|
+
| `connectGoogleAccount(successUrl, errorUrl)` | Connect a Google account |
|
|
161
|
+
| `connectFacebookAccount(successUrl, errorUrl)` | Connect a Facebook account |
|
|
162
|
+
| `oauthDisconnect(locationId, source)` | Disconnect an OAuth source |
|
|
163
|
+
| `disconnectGoogleAccount(accountId)` | Disconnect a Google account |
|
|
164
|
+
| `disconnectFacebookAccount(accountId)` | Disconnect a Facebook account |
|
|
165
|
+
| `triggerConnectedAccountMatches(accountIds)` | Trigger matching for connected accounts |
|
|
166
|
+
| `confirmConnectedAccountMatches(matchIds)` | Confirm matched listings |
|
|
167
|
+
| `connectListing(locationId, listingId, accountId)` | Connect a listing manually |
|
|
168
|
+
| `disconnectListing(locationId, source)` | Disconnect a listing |
|
|
169
|
+
| `createGmbListing(locationId, accountId)` | Create a Google Business listing |
|
|
170
|
+
|
|
171
|
+
### Campaigns
|
|
172
|
+
|
|
173
|
+
| Method | Description |
|
|
174
|
+
|--------|-------------|
|
|
175
|
+
| `fetchReviewCampaigns(locationId, opts)` | List review solicitation campaigns |
|
|
176
|
+
| `fetchReviewCampaignCustomers(campaignId)` | Get customers in a campaign |
|
|
177
|
+
| `createReviewCampaign(opts)` | Create a new campaign |
|
|
178
|
+
| `addReviewCampaignCustomers(campaignId, customers)` | Add customers to a campaign |
|
|
179
|
+
|
|
180
|
+
### Grid Rank
|
|
181
|
+
|
|
182
|
+
| Method | Description |
|
|
183
|
+
|--------|-------------|
|
|
184
|
+
| `fetchLocationGridReports(locationId, opts)` | List grid rank reports |
|
|
185
|
+
| `fetchGridReport(reportId)` | Get a specific grid rank report |
|
|
186
|
+
| `createGridReport(opts)` | Create a new grid rank report |
|
|
187
|
+
|
|
188
|
+
### Users
|
|
189
|
+
|
|
190
|
+
| Method | Description |
|
|
191
|
+
|--------|-------------|
|
|
192
|
+
| `fetchUsers()` | List all users |
|
|
193
|
+
| `fetchUsersByIds(ids)` | Fetch specific users by ID |
|
|
194
|
+
| `fetchRoles()` | List available roles |
|
|
195
|
+
| `fetchUserResources(userId)` | Get resources assigned to a user |
|
|
196
|
+
| `createUser(data)` | Create a new user |
|
|
197
|
+
| `updateUser(data)` | Update an existing user |
|
|
198
|
+
| `addUserLocations(userId, locationIds)` | Assign locations to a user |
|
|
199
|
+
| `removeUserLocations(userId, locationIds)` | Remove location assignments |
|
|
200
|
+
| `addUserFolders(userId, folderIds)` | Assign folders to a user |
|
|
201
|
+
| `removeUserFolders(userId, folderIds)` | Remove folder assignments |
|
|
202
|
+
| `addUserAndFolder(data)` | Create a user with a folder in one call |
|
|
203
|
+
|
|
204
|
+
## Pagination
|
|
205
|
+
|
|
206
|
+
Methods that return lists support cursor-based pagination:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Single page
|
|
210
|
+
const page = await client.fetchAllLocations({ first: 25 });
|
|
211
|
+
console.log(page.locations); // Location[]
|
|
212
|
+
console.log(page.pageInfo); // { hasNextPage, endCursor, ... }
|
|
213
|
+
|
|
214
|
+
// Next page
|
|
215
|
+
const next = await client.fetchAllLocations({
|
|
216
|
+
first: 25,
|
|
217
|
+
after: page.pageInfo.endCursor,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// All pages at once
|
|
221
|
+
const all = await client.fetchAllLocations({ fetchAll: true, pageSize: 100 });
|
|
222
|
+
// Returns Location[] (flat array)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Error Handling
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { SynupClient, SynupAPIError } from 'synup-js';
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
await client.fetchAllLocations();
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error instanceof SynupAPIError) {
|
|
234
|
+
console.log(error.statusCode); // 401, 404, 500, etc.
|
|
235
|
+
console.log(error.responseBody); // Raw API response
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Testing
|
|
241
|
+
|
|
242
|
+
### Unit Tests
|
|
243
|
+
|
|
244
|
+
Run the full unit test suite (191 tests with mocked HTTP -- no API key needed):
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npm test
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
All HTTP calls are stubbed so these tests run fast and offline. They verify request construction, response parsing, pagination logic, and error handling for every SDK method.
|
|
251
|
+
|
|
252
|
+
### Integration Tests
|
|
253
|
+
|
|
254
|
+
Run integration tests against the real Synup API (94 tests):
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
SYNUP_API_KEY=your_key npm run test:integration
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Integration tests exercise every SDK method against the live API using your account data. Read-only methods run unconditionally. Safe mutation tests (folder create/rename/delete, tag add/remove, keyword add/archive) run by default and clean up after themselves.
|
|
261
|
+
|
|
262
|
+
### Destructive Integration Tests
|
|
263
|
+
|
|
264
|
+
Some tests create and archive real locations. These are skipped by default and must be opted into:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
RUN_DESTRUCTIVE=1 SYNUP_API_KEY=your_key npm run test:integration
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The destructive suite covers the full location lifecycle: create, update, archive, activate, and cancel-archive. Test locations are archived at the end to avoid leaving stale data.
|
|
271
|
+
|
|
272
|
+
## TypeScript Support
|
|
273
|
+
|
|
274
|
+
synup-js is written in TypeScript and ships with full type definitions. It works in both JavaScript and TypeScript projects with no additional configuration. Types are included in the package -- there is no separate `@types/synup-js` to install.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { SynupClient, SynupAPIError } from 'synup-js';
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Features
|
|
281
|
+
|
|
282
|
+
- **TypeScript-first** -- Full type definitions for all inputs and responses
|
|
283
|
+
- **Zero dependencies** -- Uses native `fetch` (Node 18+)
|
|
284
|
+
- **Auto-pagination** -- Pass `fetchAll: true` to any paginated method
|
|
285
|
+
- **Automatic ID encoding** -- Pass numeric location IDs; base64 encoding is handled automatically
|
|
286
|
+
- **ESM + CJS** -- Works with both module systems
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|