zernio-sdk 0.0.821 → 0.0.823
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.
- checksums.yaml +4 -4
- data/README.md +7 -0
- data/docs/AnalyticsApi.md +78 -0
- data/docs/AnalyticsDeltaEntry.md +34 -0
- data/docs/AnalyticsDeltaEntryMetrics.md +44 -0
- data/docs/AnalyticsDeltaResponse.md +22 -0
- data/docs/BoostPostRequest.md +3 -1
- data/docs/CreateCallAdRequest.md +3 -1
- data/docs/CreateMessagingAdRequest.md +3 -1
- data/docs/CreateStandaloneAdRequest.md +3 -1
- data/docs/CtwaAdRequestBody.md +4 -2
- data/docs/WebhookEventsApi.md +69 -0
- data/docs/WebhookPayloadAnalyticsSynced.md +26 -0
- data/docs/WebhookPayloadAnalyticsSyncedAccount.md +24 -0
- data/docs/WebhookPayloadAnalyticsSyncedSync.md +20 -0
- data/lib/zernio-sdk/api/analytics_api.rb +77 -0
- data/lib/zernio-sdk/api/webhook_events_api.rb +66 -0
- data/lib/zernio-sdk/models/analytics_delta_entry.rb +380 -0
- data/lib/zernio-sdk/models/analytics_delta_entry_metrics.rb +507 -0
- data/lib/zernio-sdk/models/analytics_delta_response.rb +221 -0
- data/lib/zernio-sdk/models/boost_post_request.rb +14 -2
- data/lib/zernio-sdk/models/create_call_ad_request.rb +14 -2
- data/lib/zernio-sdk/models/create_messaging_ad_request.rb +14 -2
- data/lib/zernio-sdk/models/create_standalone_ad_request.rb +14 -2
- data/lib/zernio-sdk/models/ctwa_ad_request_body.rb +17 -5
- data/lib/zernio-sdk/models/webhook_payload_analytics_synced.rb +295 -0
- data/lib/zernio-sdk/models/webhook_payload_analytics_synced_account.rb +244 -0
- data/lib/zernio-sdk/models/webhook_payload_analytics_synced_sync.rb +193 -0
- data/lib/zernio-sdk/version.rb +1 -1
- data/lib/zernio-sdk.rb +6 -0
- data/openapi.yaml +392 -10
- data/spec/api/analytics_api_spec.rb +15 -0
- data/spec/models/analytics_delta_entry_metrics_spec.rb +114 -0
- data/spec/models/analytics_delta_entry_spec.rb +84 -0
- data/spec/models/analytics_delta_response_spec.rb +48 -0
- data/spec/models/boost_post_request_spec.rb +6 -0
- data/spec/models/create_call_ad_request_spec.rb +6 -0
- data/spec/models/create_messaging_ad_request_spec.rb +6 -0
- data/spec/models/create_standalone_ad_request_spec.rb +6 -0
- data/spec/models/create_webhook_settings_request_spec.rb +1 -1
- data/spec/models/ctwa_ad_request_body_spec.rb +6 -0
- data/spec/models/update_webhook_settings_request_spec.rb +1 -1
- data/spec/models/webhook_payload_analytics_synced_account_spec.rb +54 -0
- data/spec/models/webhook_payload_analytics_synced_spec.rb +64 -0
- data/spec/models/webhook_payload_analytics_synced_sync_spec.rb +42 -0
- data/spec/models/webhook_spec.rb +1 -1
- metadata +1773 -1749
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c973ced2b792862d1674a18b816c13e6072ee715199b2d7b977253b97f037a3b
|
|
4
|
+
data.tar.gz: c714a9953e07c2cb42774b5a36742630af7d5fbe0ba68a8faef919b603ef0e7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf1b2ff18f7fcf39318fe9825b3c549facb294e79ef4b0dd23f539caa2d86916c1e46935d870dae8f405d8ff72e9a5e88ef2fbce913bc721802845a520b9531d
|
|
7
|
+
data.tar.gz: 8155282bf8ce47d7e4aad32a0128c92360ed12d21b171f881d8a5e8e0cf2e28f0993c54f3e9de72666c7030f770f3c64e178b89b31ee0ba6f6f05724f9fb7ad4
|
data/README.md
CHANGED
|
@@ -188,6 +188,7 @@ Class | Method | HTTP request | Description
|
|
|
188
188
|
*Zernio::AdTargetingApi* | [**search_ad_interests**](docs/AdTargetingApi.md#search_ad_interests) | **GET** /v1/ads/interests | Search targeting interests
|
|
189
189
|
*Zernio::AdTargetingApi* | [**search_ad_targeting**](docs/AdTargetingApi.md#search_ad_targeting) | **GET** /v1/ads/targeting/search | Search targeting options
|
|
190
190
|
*Zernio::AnalyticsApi* | [**get_analytics**](docs/AnalyticsApi.md#get_analytics) | **GET** /v1/analytics | Get post analytics
|
|
191
|
+
*Zernio::AnalyticsApi* | [**get_analytics_delta**](docs/AnalyticsApi.md#get_analytics_delta) | **GET** /v1/analytics/delta | Analytics changed since a cursor
|
|
191
192
|
*Zernio::AnalyticsApi* | [**get_best_time_to_post**](docs/AnalyticsApi.md#get_best_time_to_post) | **GET** /v1/analytics/best-time | Get best times to post
|
|
192
193
|
*Zernio::AnalyticsApi* | [**get_content_decay**](docs/AnalyticsApi.md#get_content_decay) | **GET** /v1/analytics/content-decay | Get content performance decay
|
|
193
194
|
*Zernio::AnalyticsApi* | [**get_daily_metrics**](docs/AnalyticsApi.md#get_daily_metrics) | **GET** /v1/analytics/daily-metrics | Get daily aggregated metrics
|
|
@@ -731,6 +732,9 @@ Class | Method | HTTP request | Description
|
|
|
731
732
|
- [Zernio::AdsListResponse](docs/AdsListResponse.md)
|
|
732
733
|
- [Zernio::AdsTimelineResponse](docs/AdsTimelineResponse.md)
|
|
733
734
|
- [Zernio::AdsTimelineResponseRowsInner](docs/AdsTimelineResponseRowsInner.md)
|
|
735
|
+
- [Zernio::AnalyticsDeltaEntry](docs/AnalyticsDeltaEntry.md)
|
|
736
|
+
- [Zernio::AnalyticsDeltaEntryMetrics](docs/AnalyticsDeltaEntryMetrics.md)
|
|
737
|
+
- [Zernio::AnalyticsDeltaResponse](docs/AnalyticsDeltaResponse.md)
|
|
734
738
|
- [Zernio::AnalyticsListResponse](docs/AnalyticsListResponse.md)
|
|
735
739
|
- [Zernio::AnalyticsListResponsePostsInner](docs/AnalyticsListResponsePostsInner.md)
|
|
736
740
|
- [Zernio::AnalyticsListResponsePostsInnerMediaItemsInner](docs/AnalyticsListResponsePostsInnerMediaItemsInner.md)
|
|
@@ -2302,6 +2306,9 @@ Class | Method | HTTP request | Description
|
|
|
2302
2306
|
- [Zernio::WebhookPayloadAdStatusChangedAdObject](docs/WebhookPayloadAdStatusChangedAdObject.md)
|
|
2303
2307
|
- [Zernio::WebhookPayloadAdStatusChangedError](docs/WebhookPayloadAdStatusChangedError.md)
|
|
2304
2308
|
- [Zernio::WebhookPayloadAdStatusChangedStatus](docs/WebhookPayloadAdStatusChangedStatus.md)
|
|
2309
|
+
- [Zernio::WebhookPayloadAnalyticsSynced](docs/WebhookPayloadAnalyticsSynced.md)
|
|
2310
|
+
- [Zernio::WebhookPayloadAnalyticsSyncedAccount](docs/WebhookPayloadAnalyticsSyncedAccount.md)
|
|
2311
|
+
- [Zernio::WebhookPayloadAnalyticsSyncedSync](docs/WebhookPayloadAnalyticsSyncedSync.md)
|
|
2305
2312
|
- [Zernio::WebhookPayloadCallEnded](docs/WebhookPayloadCallEnded.md)
|
|
2306
2313
|
- [Zernio::WebhookPayloadCallEndedCall](docs/WebhookPayloadCallEndedCall.md)
|
|
2307
2314
|
- [Zernio::WebhookPayloadCallEndedCallBilling](docs/WebhookPayloadCallEndedCallBilling.md)
|
data/docs/AnalyticsApi.md
CHANGED
|
@@ -5,6 +5,7 @@ All URIs are relative to *https://zernio.com/api*
|
|
|
5
5
|
| Method | HTTP request | Description |
|
|
6
6
|
| ------ | ------------ | ----------- |
|
|
7
7
|
| [**get_analytics**](AnalyticsApi.md#get_analytics) | **GET** /v1/analytics | Get post analytics |
|
|
8
|
+
| [**get_analytics_delta**](AnalyticsApi.md#get_analytics_delta) | **GET** /v1/analytics/delta | Analytics changed since a cursor |
|
|
8
9
|
| [**get_best_time_to_post**](AnalyticsApi.md#get_best_time_to_post) | **GET** /v1/analytics/best-time | Get best times to post |
|
|
9
10
|
| [**get_content_decay**](AnalyticsApi.md#get_content_decay) | **GET** /v1/analytics/content-decay | Get content performance decay |
|
|
10
11
|
| [**get_daily_metrics**](AnalyticsApi.md#get_daily_metrics) | **GET** /v1/analytics/daily-metrics | Get daily aggregated metrics |
|
|
@@ -122,6 +123,83 @@ end
|
|
|
122
123
|
- **Accept**: application/json
|
|
123
124
|
|
|
124
125
|
|
|
126
|
+
## get_analytics_delta
|
|
127
|
+
|
|
128
|
+
> <AnalyticsDeltaResponse> get_analytics_delta(opts)
|
|
129
|
+
|
|
130
|
+
Analytics changed since a cursor
|
|
131
|
+
|
|
132
|
+
Cursor feed of the analytics snapshots that CHANGED, across every account you can read, in one paginated stream. Built for integrations that would otherwise call `GET /v1/analytics` once per connected account. Each page carries changes from many accounts at once, so your call count scales with how much actually changed rather than with how many accounts you have. Measured against a fleet of roughly 1,600 connected accounts: about 1,599 per-account analytics calls an hour became about 205 delta calls an hour, a 7.8x reduction. **Bootstrap once, then stay in sync.** Load your baseline from `GET /v1/analytics`, which is the historical endpoint. This one is a rolling 7-day change log and cannot replay history. Then call this endpoint with NO `cursor`: it answers with an empty `data` array plus the feed's current position in `nextCursor`. Send that `nextCursor` back on the next call and you receive everything written since. `nextCursor` is present on every response, empty pages included, so you always have something to advance with. **Ordering.** Entries come back oldest first, in the order the feed received them. That order is NOT `syncedAt`: `syncedAt` is stamped when an account's sync cycle started, and a slow cycle writes its rows after a faster cycle that started later, so `syncedAt` can go backwards between consecutive entries. Do not sort, filter or resume on it. The cursor is the only stable position, and it is opaque: pass it back verbatim, and do not parse, construct or compare cursors. **`hasMore: false` does not mean the feed ended.** This stream has no end and `nextCursor` is never null. `hasMore: true` means more changes are already waiting, so call again straight away. `hasMore: false` means you are caught up: keep the cursor and poll again on your normal interval. **The newest changes settle before they are served.** The feed deliberately holds back its last few seconds of writes, so that a row can never become visible behind a cursor you have already advanced past. A read issued the instant an `analytics.synced` webhook lands will therefore often return an empty page for that account. Do not read an empty page as \"nothing changed\": poll again with the SAME cursor you just used rather than advancing. **Repeats inside one instant.** A sync cycle occasionally records the same post twice at the same feed position. When that happens the feed delivers one of those rows, not both. Measured over a day of production traffic, about 1.3% of rows fall in such a group and 99.4% of those groups are identical rows, so this is far more often deduplication than loss. Metrics are absolute values rather than increments, so a later entry for the same post supersedes an earlier one. **Retention is 7 days.** Changes older than that leave the feed. A cursor older than 6 days is rejected with a `400` (a day of margin, because expiry is lazy). Recover by re-bootstrapping from `GET /v1/analytics` and taking a fresh cursor from a call to this endpoint with no `cursor`. A consumer that polls at least daily never reaches this. Pairs with the `analytics.synced` webhook, so changes can be read on notification instead of on a timer. That event carries no cursor of its own: keep using the `nextCursor` this endpoint gave you. Requires the same analytics access as `GET /v1/analytics`, and shares the stricter per-second rate-limit window applied to analytics endpoints.
|
|
133
|
+
|
|
134
|
+
### Examples
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
require 'time'
|
|
138
|
+
require 'zernio-sdk'
|
|
139
|
+
# setup authorization
|
|
140
|
+
Zernio.configure do |config|
|
|
141
|
+
# Configure Bearer authorization (JWT): bearerAuth
|
|
142
|
+
config.access_token = 'YOUR_BEARER_TOKEN'
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
api_instance = Zernio::AnalyticsApi.new
|
|
146
|
+
opts = {
|
|
147
|
+
cursor: 'v1.WyIyMDI2LTA5LTAxIDE3OjEyOjA0IiwiNjVmMWMwYTllMmI1YWYwMDEyYWIzNGNkIl0', # String | Opaque cursor from a previous response's `nextCursor`. Omit it to start from now: the response is then an empty page carrying the feed's current position. Rejected with a `400` when malformed, or when older than the retention window.
|
|
148
|
+
limit: 56, # Integer | Page size. Out-of-range values are a 400, never a silent clamp.
|
|
149
|
+
platform: 'platform_example', # String | Filter to a single platform (for example \"youtube\"). Omit for every platform.
|
|
150
|
+
profile_id: 'profile_id_example' # String | Filter by profile ID (default \"all\"). Must be a valid profile ID or \"all\".
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
begin
|
|
154
|
+
# Analytics changed since a cursor
|
|
155
|
+
result = api_instance.get_analytics_delta(opts)
|
|
156
|
+
p result
|
|
157
|
+
rescue Zernio::ApiError => e
|
|
158
|
+
puts "Error when calling AnalyticsApi->get_analytics_delta: #{e}"
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Using the get_analytics_delta_with_http_info variant
|
|
163
|
+
|
|
164
|
+
This returns an Array which contains the response data, status code and headers.
|
|
165
|
+
|
|
166
|
+
> <Array(<AnalyticsDeltaResponse>, Integer, Hash)> get_analytics_delta_with_http_info(opts)
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
begin
|
|
170
|
+
# Analytics changed since a cursor
|
|
171
|
+
data, status_code, headers = api_instance.get_analytics_delta_with_http_info(opts)
|
|
172
|
+
p status_code # => 2xx
|
|
173
|
+
p headers # => { ... }
|
|
174
|
+
p data # => <AnalyticsDeltaResponse>
|
|
175
|
+
rescue Zernio::ApiError => e
|
|
176
|
+
puts "Error when calling AnalyticsApi->get_analytics_delta_with_http_info: #{e}"
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Parameters
|
|
181
|
+
|
|
182
|
+
| Name | Type | Description | Notes |
|
|
183
|
+
| ---- | ---- | ----------- | ----- |
|
|
184
|
+
| **cursor** | **String** | Opaque cursor from a previous response's `nextCursor`. Omit it to start from now: the response is then an empty page carrying the feed's current position. Rejected with a `400` when malformed, or when older than the retention window. | [optional] |
|
|
185
|
+
| **limit** | **Integer** | Page size. Out-of-range values are a 400, never a silent clamp. | [optional][default to 50] |
|
|
186
|
+
| **platform** | **String** | Filter to a single platform (for example \"youtube\"). Omit for every platform. | [optional] |
|
|
187
|
+
| **profile_id** | **String** | Filter by profile ID (default \"all\"). Must be a valid profile ID or \"all\". | [optional][default to 'all'] |
|
|
188
|
+
|
|
189
|
+
### Return type
|
|
190
|
+
|
|
191
|
+
[**AnalyticsDeltaResponse**](AnalyticsDeltaResponse.md)
|
|
192
|
+
|
|
193
|
+
### Authorization
|
|
194
|
+
|
|
195
|
+
[bearerAuth](../README.md#bearerAuth)
|
|
196
|
+
|
|
197
|
+
### HTTP request headers
|
|
198
|
+
|
|
199
|
+
- **Content-Type**: Not defined
|
|
200
|
+
- **Accept**: application/json
|
|
201
|
+
|
|
202
|
+
|
|
125
203
|
## get_best_time_to_post
|
|
126
204
|
|
|
127
205
|
> <GetBestTimeToPost200Response> get_best_time_to_post(opts)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Zernio::AnalyticsDeltaEntry
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **post_id** | **String** | External post ID. The same identifier as `posts[]._id` in GET /v1/analytics. | |
|
|
8
|
+
| **account_id** | **String** | Social account this post was published through | |
|
|
9
|
+
| **profile_id** | **String** | Profile the account belongs to | |
|
|
10
|
+
| **platform** | **String** | | |
|
|
11
|
+
| **platform_post_id** | **String** | Platform-side post ID (for example the YouTube video ID) | |
|
|
12
|
+
| **published_at** | **Time** | When the post was published, ISO-8601 UTC | |
|
|
13
|
+
| **synced_at** | **Time** | When the sync cycle that produced this snapshot STARTED, ISO-8601 UTC. This is NOT the order entries arrive in and it is not a resume point: a slow cycle writes its rows after a faster cycle that started later, so `syncedAt` can go backwards between consecutive entries. Use `nextCursor` to resume. | |
|
|
14
|
+
| **is_deleted** | **Boolean** | True when the post was detected as deleted on the platform at this sync | |
|
|
15
|
+
| **metrics** | [**AnalyticsDeltaEntryMetrics**](AnalyticsDeltaEntryMetrics.md) | | |
|
|
16
|
+
|
|
17
|
+
## Example
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require 'zernio-sdk'
|
|
21
|
+
|
|
22
|
+
instance = Zernio::AnalyticsDeltaEntry.new(
|
|
23
|
+
post_id: null,
|
|
24
|
+
account_id: null,
|
|
25
|
+
profile_id: null,
|
|
26
|
+
platform: youtube,
|
|
27
|
+
platform_post_id: null,
|
|
28
|
+
published_at: null,
|
|
29
|
+
synced_at: null,
|
|
30
|
+
is_deleted: null,
|
|
31
|
+
metrics: null
|
|
32
|
+
)
|
|
33
|
+
```
|
|
34
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Zernio::AnalyticsDeltaEntryMetrics
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **impressions** | **Integer** | | |
|
|
8
|
+
| **reach** | **Integer** | | |
|
|
9
|
+
| **likes** | **Integer** | | |
|
|
10
|
+
| **comments** | **Integer** | | |
|
|
11
|
+
| **shares** | **Integer** | | |
|
|
12
|
+
| **saves** | **Integer** | | |
|
|
13
|
+
| **sends** | **Integer** | | |
|
|
14
|
+
| **clicks** | **Integer** | | |
|
|
15
|
+
| **views** | **Integer** | | |
|
|
16
|
+
| **follows** | **Integer** | Follows attributed to this post (Instagram) | |
|
|
17
|
+
| **ig_reels_avg_watch_time** | **Integer** | Instagram Reels average watch time, in milliseconds | |
|
|
18
|
+
| **ig_reels_video_view_total_time** | **Integer** | Instagram Reels total watch time, in milliseconds | |
|
|
19
|
+
| **reposts** | **Integer** | | |
|
|
20
|
+
| **reels_skip_rate** | **Float** | Instagram Reels skip rate, 0 to 1 | |
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require 'zernio-sdk'
|
|
26
|
+
|
|
27
|
+
instance = Zernio::AnalyticsDeltaEntryMetrics.new(
|
|
28
|
+
impressions: null,
|
|
29
|
+
reach: null,
|
|
30
|
+
likes: null,
|
|
31
|
+
comments: null,
|
|
32
|
+
shares: null,
|
|
33
|
+
saves: null,
|
|
34
|
+
sends: null,
|
|
35
|
+
clicks: null,
|
|
36
|
+
views: null,
|
|
37
|
+
follows: null,
|
|
38
|
+
ig_reels_avg_watch_time: null,
|
|
39
|
+
ig_reels_video_view_total_time: null,
|
|
40
|
+
reposts: null,
|
|
41
|
+
reels_skip_rate: null
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Zernio::AnalyticsDeltaResponse
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **data** | [**Array<AnalyticsDeltaEntry>**](AnalyticsDeltaEntry.md) | Changed snapshots, oldest first, in the order the feed received them. Empty on the bootstrap call (no `cursor` supplied) and whenever nothing has changed since your cursor. | |
|
|
8
|
+
| **next_cursor** | **String** | Cursor to send on the next call. ALWAYS present, including on an empty page, so you always have something to advance with, and it never moves backwards. Opaque: pass it back verbatim, and do not parse, construct or compare cursors. | |
|
|
9
|
+
| **has_more** | **Boolean** | True when more changes are already waiting past `nextCursor`, so call again immediately. False means you are caught up: keep `nextCursor` and poll again later. This feed never ends, so `hasMore: false` does NOT mean `nextCursor` is null. | |
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require 'zernio-sdk'
|
|
15
|
+
|
|
16
|
+
instance = Zernio::AnalyticsDeltaResponse.new(
|
|
17
|
+
data: null,
|
|
18
|
+
next_cursor: v1.WyIyMDI2LTA5LTAxIDE3OjEyOjA0IiwiNjVmMWMwYTllMmI1YWYwMDEyYWIzNGNkIl0,
|
|
19
|
+
has_more: null
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
data/docs/BoostPostRequest.md
CHANGED
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
| **tracking** | [**BoostPostRequestTracking**](BoostPostRequestTracking.md) | | [optional] |
|
|
26
26
|
| **special_ad_categories** | **Array<String>** | Meta only. Required for housing, employment, credit, or political ads. | [optional] |
|
|
27
27
|
| **special_ad_category_country** | **Array<String>** | Meta (metaads) only. 2-letter ISO country codes the special ad category applies to. Requires specialAdCategories to be set (400 otherwise). | [optional] |
|
|
28
|
-
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV). Forwarded to the ad set. | [optional] |
|
|
28
|
+
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV). Forwarded to the ad set. | [optional] |
|
|
29
|
+
| **regional_regulation_identities** | **Hash<String, Integer>** | Meta only. Beneficiary/payer entity IDs for regionalRegulatedCategories. Values are numeric IDs from Meta verification. Keys vary by category (e.g. universal_beneficiary / universal_payer for BRAZIL_REGULATION and THAILAND_UNIVERSAL). If omitted, Meta uses Ads Manager defaults when configured. | [optional] |
|
|
29
30
|
| **link_url** | **String** | Destination URL for the CTA button. Send it together with `callToAction`. **Meta**: adds a top-level `call_to_action` to the post-reference creative. This is what gives a `traffic` boost a clickable destination without replacing the creative and losing the post's social proof. Ignored when `leadGenFormId` is set, which supplies its own destination. Live-verified against a Page-post creative. **TikTok**: maps to `landing_page_url` on the Spark Ad creative (`AdcreateCreatives.landing_page_url`); Spark Ads have no clickable destination without it. Ignored on LinkedIn / Pinterest / X / Google, which infer the destination from the boosted post. | [optional] |
|
|
30
31
|
| **call_to_action** | **String** | CTA button label. Send it together with `linkUrl` — a CTA without a destination produces a button that goes nowhere, so sending one alone is a 400. **Meta**: the CTA enum of POST /v1/ads/create plus `VIEW_INSTAGRAM_PROFILE`, which is accepted on boost only. For that value `linkUrl` is typically the Instagram profile URL. **TikTok**: pass-through to `call_to_action` on the Spark Ad creative; the platform validates the value. See TikTok's \"Enumeration - Call-to-Action\". | [optional] |
|
|
31
32
|
| **spark_auth_code** | **String** | TikTok-only. Spark Code (creator's `auth_code`) authorizing cross-creator Spark Ads — the advertiser can boost a video owned by a DIFFERENT TikTok account. Without this, boosts are limited to videos owned by the same account running the ads (same-BC creators only). The creator generates the code in their TikTok app's Promote settings and shares it with the advertiser. Maps to `auth_code` on the creative entry of /v2/ad/create/. | [optional] |
|
|
@@ -63,6 +64,7 @@ instance = Zernio::BoostPostRequest.new(
|
|
|
63
64
|
special_ad_categories: null,
|
|
64
65
|
special_ad_category_country: null,
|
|
65
66
|
regional_regulated_categories: null,
|
|
67
|
+
regional_regulation_identities: null,
|
|
66
68
|
link_url: null,
|
|
67
69
|
call_to_action: null,
|
|
68
70
|
spark_auth_code: null,
|
data/docs/CreateCallAdRequest.md
CHANGED
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
| **roas_average_floor** | **Float** | Decimal ROAS multiplier (e.g. `2.0` = 2.0× ROAS floor). Required when `bidStrategy` is `LOWEST_COST_WITH_MIN_ROAS`; rejected otherwise. Meta enforces its own upper bound server-side. | [optional] |
|
|
39
39
|
| **dsa_beneficiary** | **String** | Legal entity that benefits from the ad. Required when targeting EU users (EU DSA, Article 26). Optional if the ad account has a default beneficiary: set it once via `PATCH /v1/ads/accounts` or in Meta Ads Manager, and Meta fills it in whenever the field is omitted. | [optional] |
|
|
40
40
|
| **dsa_payor** | **String** | Legal entity that pays for the ad. Can differ from `dsaBeneficiary` (for example, an agency paying for a client's ads). Same rules as `dsaBeneficiary`: required for EU targeting unless the ad account has a default payor. | [optional] |
|
|
41
|
-
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV). Forwarded to the ad set. | [optional] |
|
|
41
|
+
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV). Forwarded to the ad set. | [optional] |
|
|
42
|
+
| **regional_regulation_identities** | **Hash<String, Integer>** | Meta only. Beneficiary/payer entity IDs required alongside regionalRegulatedCategories. Values are numeric IDs from the advertiser's Meta verification/authorization setup. Keys depend on the declared category: BRAZIL_REGULATION and THAILAND_UNIVERSAL use universal_beneficiary / universal_payer; SINGAPORE_UNIVERSAL uses singapore_universal_beneficiary / singapore_universal_payer; TAIWAN_UNIVERSAL uses taiwan_universal_beneficiary / taiwan_universal_payer; TAIWAN_FINSERV uses taiwan_finserv_beneficiary / taiwan_finserv_payer; AUSTRALIA_FINSERV uses australia_finserv_beneficiary / australia_finserv_payer; INDIA_FINSERV uses india_finserv_beneficiary / india_finserv_payer. Both beneficiary and payer must be included. If omitted and the advertiser has set defaults in Meta Ads Manager advertising settings, Meta auto-fills them. | [optional] |
|
|
42
43
|
| **phone_number** | **String** | E.164 number the CALL_NOW CTA dials (e.g. +34600111222). | |
|
|
43
44
|
| **link_url** | **String** | Website shown as the creative's link. Required: Meta rejects tel: as link_data.link; the phone number rides only the CTA. | |
|
|
44
45
|
|
|
@@ -83,6 +84,7 @@ instance = Zernio::CreateCallAdRequest.new(
|
|
|
83
84
|
dsa_beneficiary: null,
|
|
84
85
|
dsa_payor: null,
|
|
85
86
|
regional_regulated_categories: null,
|
|
87
|
+
regional_regulation_identities: null,
|
|
86
88
|
phone_number: null,
|
|
87
89
|
link_url: null
|
|
88
90
|
)
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
| **roas_average_floor** | **Float** | Decimal ROAS multiplier (e.g. `2.0` = 2.0× ROAS floor). Required when `bidStrategy` is `LOWEST_COST_WITH_MIN_ROAS`; rejected otherwise. Meta enforces its own upper bound server-side. | [optional] |
|
|
39
39
|
| **dsa_beneficiary** | **String** | Legal entity that benefits from the ad. Required when targeting EU users (EU DSA, Article 26). Optional if the ad account has a default beneficiary: set it once via `PATCH /v1/ads/accounts` or in Meta Ads Manager, and Meta fills it in whenever the field is omitted. | [optional] |
|
|
40
40
|
| **dsa_payor** | **String** | Legal entity that pays for the ad. Can differ from `dsaBeneficiary` (for example, an agency paying for a client's ads). Same rules as `dsaBeneficiary`: required for EU targeting unless the ad account has a default payor. | [optional] |
|
|
41
|
-
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV). Forwarded to the ad set. | [optional] |
|
|
41
|
+
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV). Forwarded to the ad set. | [optional] |
|
|
42
|
+
| **regional_regulation_identities** | **Hash<String, Integer>** | Meta only. Beneficiary/payer entity IDs required alongside regionalRegulatedCategories. Values are numeric IDs from the advertiser's Meta verification/authorization setup. Keys depend on the declared category: BRAZIL_REGULATION and THAILAND_UNIVERSAL use universal_beneficiary / universal_payer; SINGAPORE_UNIVERSAL uses singapore_universal_beneficiary / singapore_universal_payer; TAIWAN_UNIVERSAL uses taiwan_universal_beneficiary / taiwan_universal_payer; TAIWAN_FINSERV uses taiwan_finserv_beneficiary / taiwan_finserv_payer; AUSTRALIA_FINSERV uses australia_finserv_beneficiary / australia_finserv_payer; INDIA_FINSERV uses india_finserv_beneficiary / india_finserv_payer. Both beneficiary and payer must be included. If omitted and the advertiser has set defaults in Meta Ads Manager advertising settings, Meta auto-fills them. | [optional] |
|
|
42
43
|
| **destination** | **String** | Where the conversation opens when the ad is tapped. | |
|
|
43
44
|
|
|
44
45
|
## Example
|
|
@@ -82,6 +83,7 @@ instance = Zernio::CreateMessagingAdRequest.new(
|
|
|
82
83
|
dsa_beneficiary: null,
|
|
83
84
|
dsa_payor: null,
|
|
84
85
|
regional_regulated_categories: null,
|
|
86
|
+
regional_regulation_identities: null,
|
|
85
87
|
destination: null
|
|
86
88
|
)
|
|
87
89
|
```
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
| **raw_targeting** | **Hash<String, Object>** | Meta only. A raw Meta-native targeting spec (snake_case: `geo_locations`, `age_min`, `excluded_custom_audiences`, `flexible_spec`, `targeting_automation`, `user_os`, `wireless_carrier`, business places, etc.) — exactly the shape `GET /v1/ads/{adId}` returns for external ads. Sent alone it reaches the ad set VERBATIM (the clone-a-campaign's-targeting-exactly path). Meta validates and surfaces any errors. Can be combined with the camelCase targeting fields (countries/regions/cities/interests/ageMin/..., `targeting`, `savedTargetingId`, `audienceId`): rawTargeting is the BASE layer and the built camelCase spec is merged on top, key by key, with the camelCase side winning on collision (the camelCase precedence chain stays `savedTargetingId` < `targeting` < flat fields). The merge goes one level deep inside `geo_locations` and `excluded_geo_locations`: built sub-keys win, raw-only sub-keys such as `location_types` survive alongside built `countries`. Array values (`flexible_spec`, ...) are replaced as a WHOLE key when the camelCase spec builds them, never element-merged. When rawTargeting is present the defaults the camelCase builder normally injects (US geo, `targeting_automation.advantage_audience: 0`) are suppressed, so raw's values are not clobbered — include `targeting_automation` in the raw spec (or send `advantageAudience`) as Meta requires it on create. If cloning an EU campaign, also pass `dsaBeneficiary` / `dsaPayor` (those are separate fields, not part of targeting). | [optional] |
|
|
64
64
|
| **special_ad_categories** | **Array<String>** | Meta only. Declares the ad's special category, required for housing, employment, credit, or political/social-issue ads (Meta enforces restricted targeting for these). Note: setting a special category disables income/zip targeting on Meta. | [optional] |
|
|
65
65
|
| **special_ad_category_country** | **Array<String>** | Meta (metaads) only. 2-letter ISO country codes the special ad category applies to. Requires specialAdCategories to be set (400 otherwise). Ignored when joining an existing campaign via existingCampaignId (the existing campaign's category/country already governs it). | [optional] |
|
|
66
|
-
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries. Known values: SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV. Meta rejects the ad set without this when the targeting geo includes the corresponding country. | [optional] |
|
|
66
|
+
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries. Known values: BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV. Meta rejects the ad set without this when the targeting geo includes the corresponding country. | [optional] |
|
|
67
|
+
| **regional_regulation_identities** | **Hash<String, Integer>** | Meta only. Beneficiary/payer entity IDs for regionalRegulatedCategories. Values are numeric IDs from Meta verification. Keys vary by category (e.g. universal_beneficiary / universal_payer for BRAZIL_REGULATION and THAILAND_UNIVERSAL). If omitted, Meta uses Ads Manager defaults when configured. | [optional] |
|
|
67
68
|
| **end_date** | **Time** | Required for lifetime budgets | [optional] |
|
|
68
69
|
| **start_date** | **Time** | Meta only. Ad-set start time (ISO 8601, e.g. \"2026-06-10T09:00:00Z\"), mapped to the ad set's `start_time`. When omitted the ad starts delivering immediately. For lifetime budgets Meta also requires `endDate`. (Same `schedule.startDate` semantics already available on `POST /v1/ads/boost`.) | [optional] |
|
|
69
70
|
| **instagram_account_id** | **String** | Meta only. Override the Instagram account the ad is delivered as — pass an Instagram Business Account ID (e.g. 17841...), mapped to the creative's `instagram_user_id`. When omitted we use the Instagram actor Meta already runs the Page's other ads as, falling back to the Page's page-backed Instagram account. Useful when a Page has more than one eligible IG account. | [optional] |
|
|
@@ -163,6 +164,7 @@ instance = Zernio::CreateStandaloneAdRequest.new(
|
|
|
163
164
|
special_ad_categories: null,
|
|
164
165
|
special_ad_category_country: null,
|
|
165
166
|
regional_regulated_categories: null,
|
|
167
|
+
regional_regulation_identities: null,
|
|
166
168
|
end_date: null,
|
|
167
169
|
start_date: null,
|
|
168
170
|
instagram_account_id: null,
|
data/docs/CtwaAdRequestBody.md
CHANGED
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
| **roas_average_floor** | **Float** | Decimal ROAS multiplier (e.g. `2.0` = 2.0× ROAS floor). Required when `bidStrategy` is `LOWEST_COST_WITH_MIN_ROAS`; rejected otherwise. Meta enforces its own upper bound server-side. | [optional] |
|
|
39
39
|
| **dsa_beneficiary** | **String** | Legal entity that benefits from the ad. Required when targeting EU users (EU DSA, Article 26). Optional if the ad account has a default beneficiary: set it once via `PATCH /v1/ads/accounts` or in Meta Ads Manager, and Meta fills it in whenever the field is omitted. | [optional] |
|
|
40
40
|
| **dsa_payor** | **String** | Legal entity that pays for the ad. Can differ from `dsaBeneficiary` (for example, an agency paying for a client's ads). Same rules as `dsaBeneficiary`: required for EU targeting unless the ad account has a default payor. | [optional] |
|
|
41
|
-
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV). Forwarded to the ad set. | [optional] |
|
|
41
|
+
| **regional_regulated_categories** | **Array<String>** | Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. BRAZIL_REGULATION, SINGAPORE_UNIVERSAL, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL, AUSTRALIA_FINSERV, INDIA_FINSERV, TAIWAN_FINSERV). Forwarded to the ad set. | [optional] |
|
|
42
|
+
| **regional_regulation_identities** | **Hash<String, Integer>** | Meta only. Beneficiary/payer entity IDs required alongside regionalRegulatedCategories. Values are numeric IDs from the advertiser's Meta verification/authorization setup. Keys depend on the declared category: BRAZIL_REGULATION and THAILAND_UNIVERSAL use universal_beneficiary / universal_payer; SINGAPORE_UNIVERSAL uses singapore_universal_beneficiary / singapore_universal_payer; TAIWAN_UNIVERSAL uses taiwan_universal_beneficiary / taiwan_universal_payer; TAIWAN_FINSERV uses taiwan_finserv_beneficiary / taiwan_finserv_payer; AUSTRALIA_FINSERV uses australia_finserv_beneficiary / australia_finserv_payer; INDIA_FINSERV uses india_finserv_beneficiary / india_finserv_payer. Both beneficiary and payer must be included. If omitted and the advertiser has set defaults in Meta Ads Manager advertising settings, Meta auto-fills them. | [optional] |
|
|
42
43
|
|
|
43
44
|
## Example
|
|
44
45
|
|
|
@@ -80,7 +81,8 @@ instance = Zernio::CtwaAdRequestBody.new(
|
|
|
80
81
|
roas_average_floor: null,
|
|
81
82
|
dsa_beneficiary: null,
|
|
82
83
|
dsa_payor: null,
|
|
83
|
-
regional_regulated_categories: null
|
|
84
|
+
regional_regulated_categories: null,
|
|
85
|
+
regional_regulation_identities: null
|
|
84
86
|
)
|
|
85
87
|
```
|
|
86
88
|
|
data/docs/WebhookEventsApi.md
CHANGED
|
@@ -8,6 +8,7 @@ All URIs are relative to *https://zernio.com/api*
|
|
|
8
8
|
| [**on_account_connected**](WebhookEventsApi.md#on_account_connected) | **POST** /account.connected | Account connected event |
|
|
9
9
|
| [**on_account_disconnected**](WebhookEventsApi.md#on_account_disconnected) | **POST** /account.disconnected | Account disconnected event |
|
|
10
10
|
| [**on_ad_status_changed**](WebhookEventsApi.md#on_ad_status_changed) | **POST** /ad.status_changed | Ad status changed event |
|
|
11
|
+
| [**on_analytics_synced**](WebhookEventsApi.md#on_analytics_synced) | **POST** /analytics.synced | Analytics synced event |
|
|
11
12
|
| [**on_call_ended**](WebhookEventsApi.md#on_call_ended) | **POST** /call.ended | Call ended event |
|
|
12
13
|
| [**on_call_failed**](WebhookEventsApi.md#on_call_failed) | **POST** /call.failed | Call failed event |
|
|
13
14
|
| [**on_call_permission_request**](WebhookEventsApi.md#on_call_permission_request) | **POST** /call.permission_request | Call permission request reply event |
|
|
@@ -329,6 +330,74 @@ nil (empty response body)
|
|
|
329
330
|
- **Accept**: Not defined
|
|
330
331
|
|
|
331
332
|
|
|
333
|
+
## on_analytics_synced
|
|
334
|
+
|
|
335
|
+
> on_analytics_synced(webhook_payload_analytics_synced)
|
|
336
|
+
|
|
337
|
+
Analytics synced event
|
|
338
|
+
|
|
339
|
+
Fired once per connected account each time its analytics sync cycle completes successfully. Poll-driven (roughly hourly per account), not real-time, and never fired for a skipped or failed cycle. A trigger, not a transport: the payload carries no metrics and no cursor. On receipt, call `GET /v1/analytics/delta` with your own last `nextCursor` to read every post whose analytics changed, across every account, in one paginated stream instead of polling analytics once per account. The feed holds back its most recent few seconds of writes, so a read issued the instant this event lands often returns an empty page for that account. Poll again with the same cursor rather than reading an empty page as \"nothing changed\". High volume (roughly one delivery per connected account per hour). Subscribe to it on a dedicated webhook endpoint: a subscription's consecutive-failure count is shared across all of its events, so an outage while this event is flowing can suppress the low-volume publishing events on the same subscription.
|
|
340
|
+
|
|
341
|
+
### Examples
|
|
342
|
+
|
|
343
|
+
```ruby
|
|
344
|
+
require 'time'
|
|
345
|
+
require 'zernio-sdk'
|
|
346
|
+
# setup authorization
|
|
347
|
+
Zernio.configure do |config|
|
|
348
|
+
# Configure Bearer authorization (JWT): bearerAuth
|
|
349
|
+
config.access_token = 'YOUR_BEARER_TOKEN'
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
api_instance = Zernio::WebhookEventsApi.new
|
|
353
|
+
webhook_payload_analytics_synced = # WebhookPayloadAnalyticsSynced |
|
|
354
|
+
|
|
355
|
+
begin
|
|
356
|
+
# Analytics synced event
|
|
357
|
+
api_instance.on_analytics_synced(webhook_payload_analytics_synced)
|
|
358
|
+
rescue Zernio::ApiError => e
|
|
359
|
+
puts "Error when calling WebhookEventsApi->on_analytics_synced: #{e}"
|
|
360
|
+
end
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Using the on_analytics_synced_with_http_info variant
|
|
364
|
+
|
|
365
|
+
This returns an Array which contains the response data (`nil` in this case), status code and headers.
|
|
366
|
+
|
|
367
|
+
> <Array(nil, Integer, Hash)> on_analytics_synced_with_http_info(webhook_payload_analytics_synced)
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
begin
|
|
371
|
+
# Analytics synced event
|
|
372
|
+
data, status_code, headers = api_instance.on_analytics_synced_with_http_info(webhook_payload_analytics_synced)
|
|
373
|
+
p status_code # => 2xx
|
|
374
|
+
p headers # => { ... }
|
|
375
|
+
p data # => nil
|
|
376
|
+
rescue Zernio::ApiError => e
|
|
377
|
+
puts "Error when calling WebhookEventsApi->on_analytics_synced_with_http_info: #{e}"
|
|
378
|
+
end
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Parameters
|
|
382
|
+
|
|
383
|
+
| Name | Type | Description | Notes |
|
|
384
|
+
| ---- | ---- | ----------- | ----- |
|
|
385
|
+
| **webhook_payload_analytics_synced** | [**WebhookPayloadAnalyticsSynced**](WebhookPayloadAnalyticsSynced.md) | | |
|
|
386
|
+
|
|
387
|
+
### Return type
|
|
388
|
+
|
|
389
|
+
nil (empty response body)
|
|
390
|
+
|
|
391
|
+
### Authorization
|
|
392
|
+
|
|
393
|
+
[bearerAuth](../README.md#bearerAuth)
|
|
394
|
+
|
|
395
|
+
### HTTP request headers
|
|
396
|
+
|
|
397
|
+
- **Content-Type**: application/json
|
|
398
|
+
- **Accept**: Not defined
|
|
399
|
+
|
|
400
|
+
|
|
332
401
|
## on_call_ended
|
|
333
402
|
|
|
334
403
|
> on_call_ended(webhook_payload_call_ended)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Zernio::WebhookPayloadAnalyticsSynced
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **id** | **String** | Stable webhook event ID | |
|
|
8
|
+
| **event** | **String** | | |
|
|
9
|
+
| **account** | [**WebhookPayloadAnalyticsSyncedAccount**](WebhookPayloadAnalyticsSyncedAccount.md) | | |
|
|
10
|
+
| **sync** | [**WebhookPayloadAnalyticsSyncedSync**](WebhookPayloadAnalyticsSyncedSync.md) | | |
|
|
11
|
+
| **timestamp** | **Time** | UTC time at which Zernio generated this event (set once when the event payload is built, before delivery is queued). | |
|
|
12
|
+
|
|
13
|
+
## Example
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
require 'zernio-sdk'
|
|
17
|
+
|
|
18
|
+
instance = Zernio::WebhookPayloadAnalyticsSynced.new(
|
|
19
|
+
id: null,
|
|
20
|
+
event: null,
|
|
21
|
+
account: null,
|
|
22
|
+
sync: null,
|
|
23
|
+
timestamp: null
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Zernio::WebhookPayloadAnalyticsSyncedAccount
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **account_id** | **String** | The account's unique identifier (same as used in /v1/accounts/{accountId}) | |
|
|
8
|
+
| **profile_id** | **String** | The profile this account belongs to | |
|
|
9
|
+
| **platform** | **String** | | |
|
|
10
|
+
| **username** | **String** | | |
|
|
11
|
+
|
|
12
|
+
## Example
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
require 'zernio-sdk'
|
|
16
|
+
|
|
17
|
+
instance = Zernio::WebhookPayloadAnalyticsSyncedAccount.new(
|
|
18
|
+
account_id: null,
|
|
19
|
+
profile_id: null,
|
|
20
|
+
platform: youtube,
|
|
21
|
+
username: null
|
|
22
|
+
)
|
|
23
|
+
```
|
|
24
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Zernio::WebhookPayloadAnalyticsSyncedSync
|
|
2
|
+
|
|
3
|
+
## Properties
|
|
4
|
+
|
|
5
|
+
| Name | Type | Description | Notes |
|
|
6
|
+
| ---- | ---- | ----------- | ----- |
|
|
7
|
+
| **synced_at** | **Time** | When the cycle COMPLETED. Not a join key for the delta feed: the rows a cycle produces carry a `syncedAt` stamped when the cycle STARTED, which is measured at around one second earlier at the median and up to a couple of minutes earlier in the tail. Correlate on `account.accountId`. | |
|
|
8
|
+
| **posts_updated** | **Integer** | Post records created or modified by this cycle. Not the number of delta feed rows the cycle produced, which the syncer does not report, so a cycle with a non-zero `postsUpdated` can still yield an empty delta page. | |
|
|
9
|
+
|
|
10
|
+
## Example
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
require 'zernio-sdk'
|
|
14
|
+
|
|
15
|
+
instance = Zernio::WebhookPayloadAnalyticsSyncedSync.new(
|
|
16
|
+
synced_at: null,
|
|
17
|
+
posts_updated: 42
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
@@ -133,6 +133,83 @@ module Zernio
|
|
|
133
133
|
return data, status_code, headers
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
+
# Analytics changed since a cursor
|
|
137
|
+
# Cursor feed of the analytics snapshots that CHANGED, across every account you can read, in one paginated stream. Built for integrations that would otherwise call `GET /v1/analytics` once per connected account. Each page carries changes from many accounts at once, so your call count scales with how much actually changed rather than with how many accounts you have. Measured against a fleet of roughly 1,600 connected accounts: about 1,599 per-account analytics calls an hour became about 205 delta calls an hour, a 7.8x reduction. **Bootstrap once, then stay in sync.** Load your baseline from `GET /v1/analytics`, which is the historical endpoint. This one is a rolling 7-day change log and cannot replay history. Then call this endpoint with NO `cursor`: it answers with an empty `data` array plus the feed's current position in `nextCursor`. Send that `nextCursor` back on the next call and you receive everything written since. `nextCursor` is present on every response, empty pages included, so you always have something to advance with. **Ordering.** Entries come back oldest first, in the order the feed received them. That order is NOT `syncedAt`: `syncedAt` is stamped when an account's sync cycle started, and a slow cycle writes its rows after a faster cycle that started later, so `syncedAt` can go backwards between consecutive entries. Do not sort, filter or resume on it. The cursor is the only stable position, and it is opaque: pass it back verbatim, and do not parse, construct or compare cursors. **`hasMore: false` does not mean the feed ended.** This stream has no end and `nextCursor` is never null. `hasMore: true` means more changes are already waiting, so call again straight away. `hasMore: false` means you are caught up: keep the cursor and poll again on your normal interval. **The newest changes settle before they are served.** The feed deliberately holds back its last few seconds of writes, so that a row can never become visible behind a cursor you have already advanced past. A read issued the instant an `analytics.synced` webhook lands will therefore often return an empty page for that account. Do not read an empty page as \"nothing changed\": poll again with the SAME cursor you just used rather than advancing. **Repeats inside one instant.** A sync cycle occasionally records the same post twice at the same feed position. When that happens the feed delivers one of those rows, not both. Measured over a day of production traffic, about 1.3% of rows fall in such a group and 99.4% of those groups are identical rows, so this is far more often deduplication than loss. Metrics are absolute values rather than increments, so a later entry for the same post supersedes an earlier one. **Retention is 7 days.** Changes older than that leave the feed. A cursor older than 6 days is rejected with a `400` (a day of margin, because expiry is lazy). Recover by re-bootstrapping from `GET /v1/analytics` and taking a fresh cursor from a call to this endpoint with no `cursor`. A consumer that polls at least daily never reaches this. Pairs with the `analytics.synced` webhook, so changes can be read on notification instead of on a timer. That event carries no cursor of its own: keep using the `nextCursor` this endpoint gave you. Requires the same analytics access as `GET /v1/analytics`, and shares the stricter per-second rate-limit window applied to analytics endpoints.
|
|
138
|
+
# @param [Hash] opts the optional parameters
|
|
139
|
+
# @option opts [String] :cursor Opaque cursor from a previous response's `nextCursor`. Omit it to start from now: the response is then an empty page carrying the feed's current position. Rejected with a `400` when malformed, or when older than the retention window.
|
|
140
|
+
# @option opts [Integer] :limit Page size. Out-of-range values are a 400, never a silent clamp. (default to 50)
|
|
141
|
+
# @option opts [String] :platform Filter to a single platform (for example \"youtube\"). Omit for every platform.
|
|
142
|
+
# @option opts [String] :profile_id Filter by profile ID (default \"all\"). Must be a valid profile ID or \"all\". (default to 'all')
|
|
143
|
+
# @return [AnalyticsDeltaResponse]
|
|
144
|
+
def get_analytics_delta(opts = {})
|
|
145
|
+
data, _status_code, _headers = get_analytics_delta_with_http_info(opts)
|
|
146
|
+
data
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Analytics changed since a cursor
|
|
150
|
+
# Cursor feed of the analytics snapshots that CHANGED, across every account you can read, in one paginated stream. Built for integrations that would otherwise call `GET /v1/analytics` once per connected account. Each page carries changes from many accounts at once, so your call count scales with how much actually changed rather than with how many accounts you have. Measured against a fleet of roughly 1,600 connected accounts: about 1,599 per-account analytics calls an hour became about 205 delta calls an hour, a 7.8x reduction. **Bootstrap once, then stay in sync.** Load your baseline from `GET /v1/analytics`, which is the historical endpoint. This one is a rolling 7-day change log and cannot replay history. Then call this endpoint with NO `cursor`: it answers with an empty `data` array plus the feed's current position in `nextCursor`. Send that `nextCursor` back on the next call and you receive everything written since. `nextCursor` is present on every response, empty pages included, so you always have something to advance with. **Ordering.** Entries come back oldest first, in the order the feed received them. That order is NOT `syncedAt`: `syncedAt` is stamped when an account's sync cycle started, and a slow cycle writes its rows after a faster cycle that started later, so `syncedAt` can go backwards between consecutive entries. Do not sort, filter or resume on it. The cursor is the only stable position, and it is opaque: pass it back verbatim, and do not parse, construct or compare cursors. **`hasMore: false` does not mean the feed ended.** This stream has no end and `nextCursor` is never null. `hasMore: true` means more changes are already waiting, so call again straight away. `hasMore: false` means you are caught up: keep the cursor and poll again on your normal interval. **The newest changes settle before they are served.** The feed deliberately holds back its last few seconds of writes, so that a row can never become visible behind a cursor you have already advanced past. A read issued the instant an `analytics.synced` webhook lands will therefore often return an empty page for that account. Do not read an empty page as \"nothing changed\": poll again with the SAME cursor you just used rather than advancing. **Repeats inside one instant.** A sync cycle occasionally records the same post twice at the same feed position. When that happens the feed delivers one of those rows, not both. Measured over a day of production traffic, about 1.3% of rows fall in such a group and 99.4% of those groups are identical rows, so this is far more often deduplication than loss. Metrics are absolute values rather than increments, so a later entry for the same post supersedes an earlier one. **Retention is 7 days.** Changes older than that leave the feed. A cursor older than 6 days is rejected with a `400` (a day of margin, because expiry is lazy). Recover by re-bootstrapping from `GET /v1/analytics` and taking a fresh cursor from a call to this endpoint with no `cursor`. A consumer that polls at least daily never reaches this. Pairs with the `analytics.synced` webhook, so changes can be read on notification instead of on a timer. That event carries no cursor of its own: keep using the `nextCursor` this endpoint gave you. Requires the same analytics access as `GET /v1/analytics`, and shares the stricter per-second rate-limit window applied to analytics endpoints.
|
|
151
|
+
# @param [Hash] opts the optional parameters
|
|
152
|
+
# @option opts [String] :cursor Opaque cursor from a previous response's `nextCursor`. Omit it to start from now: the response is then an empty page carrying the feed's current position. Rejected with a `400` when malformed, or when older than the retention window.
|
|
153
|
+
# @option opts [Integer] :limit Page size. Out-of-range values are a 400, never a silent clamp. (default to 50)
|
|
154
|
+
# @option opts [String] :platform Filter to a single platform (for example \"youtube\"). Omit for every platform.
|
|
155
|
+
# @option opts [String] :profile_id Filter by profile ID (default \"all\"). Must be a valid profile ID or \"all\". (default to 'all')
|
|
156
|
+
# @return [Array<(AnalyticsDeltaResponse, Integer, Hash)>] AnalyticsDeltaResponse data, response status code and response headers
|
|
157
|
+
def get_analytics_delta_with_http_info(opts = {})
|
|
158
|
+
if @api_client.config.debugging
|
|
159
|
+
@api_client.config.logger.debug 'Calling API: AnalyticsApi.get_analytics_delta ...'
|
|
160
|
+
end
|
|
161
|
+
if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
|
|
162
|
+
fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_analytics_delta, must be smaller than or equal to 100.'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
|
|
166
|
+
fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_analytics_delta, must be greater than or equal to 1.'
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# resource path
|
|
170
|
+
local_var_path = '/v1/analytics/delta'
|
|
171
|
+
|
|
172
|
+
# query parameters
|
|
173
|
+
query_params = opts[:query_params] || {}
|
|
174
|
+
query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil?
|
|
175
|
+
query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
|
|
176
|
+
query_params[:'platform'] = opts[:'platform'] if !opts[:'platform'].nil?
|
|
177
|
+
query_params[:'profileId'] = opts[:'profile_id'] if !opts[:'profile_id'].nil?
|
|
178
|
+
|
|
179
|
+
# header parameters
|
|
180
|
+
header_params = opts[:header_params] || {}
|
|
181
|
+
# HTTP header 'Accept' (if needed)
|
|
182
|
+
header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
|
|
183
|
+
|
|
184
|
+
# form parameters
|
|
185
|
+
form_params = opts[:form_params] || {}
|
|
186
|
+
|
|
187
|
+
# http body (model)
|
|
188
|
+
post_body = opts[:debug_body]
|
|
189
|
+
|
|
190
|
+
# return_type
|
|
191
|
+
return_type = opts[:debug_return_type] || 'AnalyticsDeltaResponse'
|
|
192
|
+
|
|
193
|
+
# auth_names
|
|
194
|
+
auth_names = opts[:debug_auth_names] || ['bearerAuth']
|
|
195
|
+
|
|
196
|
+
new_options = opts.merge(
|
|
197
|
+
:operation => :"AnalyticsApi.get_analytics_delta",
|
|
198
|
+
:header_params => header_params,
|
|
199
|
+
:query_params => query_params,
|
|
200
|
+
:form_params => form_params,
|
|
201
|
+
:body => post_body,
|
|
202
|
+
:auth_names => auth_names,
|
|
203
|
+
:return_type => return_type
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
|
|
207
|
+
if @api_client.config.debugging
|
|
208
|
+
@api_client.config.logger.debug "API called: AnalyticsApi#get_analytics_delta\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
|
|
209
|
+
end
|
|
210
|
+
return data, status_code, headers
|
|
211
|
+
end
|
|
212
|
+
|
|
136
213
|
# Get best times to post
|
|
137
214
|
# Returns the best times to post based on historical engagement data. Groups all published posts by day of week and hour (UTC), calculating average engagement per slot. Use this to auto-schedule posts at optimal times. Requires the Analytics add-on.
|
|
138
215
|
# @param [Hash] opts the optional parameters
|