diff --git a/CHANGELOG.md b/CHANGELOG.md index bebe9cc..775ec2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,55 @@ changes — see below. hierarchy, configuration, and async/retry/timeout behavior. ### Fixed +- **Several typed models had wrong or missing fields**, found via a platform + re-audit (ADR-022) of fixtures used to originally build them — `extra="allow"` + meant a wrong alias silently yielded `None` rather than erroring, so these + went undetected until verified against live staging response bodies: + - `TrustScoreResult`: added `source` and `created_at` (the latter arrives as + a raw epoch-milliseconds integer, not the Firestore `{_seconds,...}` shape + the shared coercion validator handles — given its own field-level + validator). `short` was already correct; an earlier pass had also added an + unused `short_code` field guessing the wire key was `shortCode` — kept for + backward compatibility but confirmed it's never actually populated. + - `NamespaceInfo`: added `can_claim_custom_domain`, `can_claim_subdomain`, + `namespace_data` (the real fields) — `upgrade_required` (kept for backward + compatibility) is never actually sent by the platform. + - `AggregateAnalytics`: added `bot_clicks_excluded` (was missing; every other + field — `clicks_by_day`, `country_breakdown`, `device_breakdown`, etc. — + was already correctly named). + - `Link`: added `geo_restriction`, `og_meta`, `is_custom`, `is_disabled`, + `disabled_reason`, `trust_score`, `trust_status`, `threats` — all present + on real responses but previously inaccessible except via `.model_extra`. + - `affiliate.get_limits()`, `custom_domains.add()`, and + `webhooks.list_event_types()` return raw, untyped dicts by design (always + have) — there's no wrong-alias risk for these three since nothing is + dropped; upgrading them to typed models would change their return type + (a breaking change) and was left out of scope for this fix. + - `AffiliateProgram` (ADR-024, a second re-audit round): `cookie_days` + (kept as the field/kwarg name for backward compatibility) now reads/writes + the correct wire key `cookieDurationDays`, not `cookieDays` — this was + wrong on both the request side (`create_program()`/`update_program()`) and + the response side. Added `merchant_id`, `max_partners`, `partner_count`, + `is_public`, `created_at`, `updated_at` (all present on the owned-program + endpoints; `discover()`'s public-summary response is a subset, which the + model already tolerates since every field is `Optional`). + `list_partners()`/`list_partnerships()`/`join()`/`get_partnership_stats()` + return raw dicts by design (same as the three above) — confirmed no change + needed; a typed `AffiliatePartnership` model is a 2.0 candidate. **Open + question sent to `awsys-orch`**: `create_program()`'s request body sends + `commissionType`/`cpcRate`/`cpaRate` (existing behavior, unchanged here) — + the platform-verified fixture's minimal create-request example shows a + single `commissionRate` field instead, which may indicate the request + shape itself is wrong too; left unchanged pending confirmation rather than + guessing at a restructure that could break working creates in a different + way. +- **`utm_templates.list()`** read `utmTemplates` off `/api/v1/me`, a field the + platform never actually populated — every call silently returned an empty + list. The platform added a real `GET /api/user/utm-templates` route (#833); + `list()` now calls it and reads its `templates` array. `create()` was already + sending the correct `source`/`medium`/`campaign` body fields — no change + needed there, the platform-side 500 that used to accompany it (#831) was a + server bug, not a client wire-format mismatch. - **`analytics.get_recent_clicks()`** called `/api/user/recent-clicks`, a path that never existed on the platform (always 404'd). Now calls `/api/user/clicks/recent` and supports a `since` parameter. diff --git a/README.md b/README.md index 7e749c2..7bceb01 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ client.namespace.release() ```python client.utm_templates.create("Launch", "newsletter", "email", "sept") -for t in client.utm_templates.list(): # derived from /api/v1/me — no dedicated list route +for t in client.utm_templates.list(): print(t.name) client.utm_templates.delete(t.id) ``` diff --git a/awsysco/async_resources/affiliate.py b/awsysco/async_resources/affiliate.py index 8d821c3..9e57f37 100644 --- a/awsysco/async_resources/affiliate.py +++ b/awsysco/async_resources/affiliate.py @@ -14,7 +14,7 @@ def __init__(self, http: AsyncHttpClient) -> None: async def create_program(self, name: str, commission_type: str, **kwargs: Any) -> AffiliateProgram: body: Dict[str, Any] = {"name": name, "commissionType": commission_type} - field_map = {"description": "description", "cpc_rate": "cpcRate", "cpa_rate": "cpaRate", "cookie_days": "cookieDays"} + field_map = {"description": "description", "cpc_rate": "cpcRate", "cpa_rate": "cpaRate", "cookie_days": "cookieDurationDays"} for k, v in kwargs.items(): body[field_map.get(k, k)] = v data = await self._http.post("/api/affiliate/programs", json=body) @@ -33,7 +33,7 @@ async def get_program(self, program_id: str) -> AffiliateProgram: async def update_program(self, program_id: str, **kwargs: Any) -> AffiliateProgram: body: Dict[str, Any] = {} - field_map = {"cpc_rate": "cpcRate", "cpa_rate": "cpaRate", "cookie_days": "cookieDays", "commission_type": "commissionType"} + field_map = {"cpc_rate": "cpcRate", "cpa_rate": "cpaRate", "cookie_days": "cookieDurationDays", "commission_type": "commissionType"} for k, v in kwargs.items(): body[field_map.get(k, k)] = v data = await self._http.patch(f"/api/affiliate/programs/{program_id}", json=body) diff --git a/awsysco/async_resources/utm_templates.py b/awsysco/async_resources/utm_templates.py index 0c42302..fc49c6e 100644 --- a/awsysco/async_resources/utm_templates.py +++ b/awsysco/async_resources/utm_templates.py @@ -13,8 +13,8 @@ def __init__(self, http: AsyncHttpClient) -> None: self._http = http async def list(self) -> List[UtmTemplate]: - resp = await self._http.get("/api/v1/me") - items = resp.get("utmTemplates", []) if isinstance(resp, dict) else [] + resp = await self._http.get("/api/user/utm-templates") + items = resp.get("templates", []) if isinstance(resp, dict) else [] return [UtmTemplate.model_validate(item) for item in items] async def create(self, name: str, source: str, medium: str, campaign: str, *, term: str = "", content: str = "") -> dict: diff --git a/awsysco/models.py b/awsysco/models.py index 1c283ff..a06acf4 100644 --- a/awsysco/models.py +++ b/awsysco/models.py @@ -5,7 +5,7 @@ from datetime import datetime, timezone from typing import Any, Dict, List, Optional, Union -from pydantic import BaseModel, ConfigDict, Field, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from pydantic.alias_generators import to_camel @@ -124,6 +124,14 @@ class Link(_CamelModel): max_clicks: Optional[int] = None expire_fallback_url: Optional[str] = None password_protected: Optional[bool] = None + geo_restriction: Optional["GeoRestriction"] = None + og_meta: Optional["OgMeta"] = None + is_custom: Optional[bool] = None + is_disabled: Optional[bool] = None + disabled_reason: Optional[str] = None + trust_score: Optional[float] = None + trust_status: Optional[str] = None + threats: Optional[List[str]] = None class LinkList(_CamelModel): @@ -294,9 +302,11 @@ class QRSettings(_CamelModel): class TrustScoreResult(_CamelModel): """Result of a URL trust/safety scan. - Wire keys are ``shortCode``/``trustScore``/``trustStatus`` — ``short``/``long`` - are kept as separate (currently unpopulated) fields since the platform doesn't - send them under those names; removing them would be a breaking change. + Wire keys are ``short``/``trustScore``/``trustStatus``/``scannedAt``/``source``/ + ``createdAt`` (platform-verified against live staging). ``short_code`` is kept + as a separate (currently unpopulated) field for backward compatibility — an + earlier version of this model guessed the wire key was ``shortCode``, which + it isn't; removing the field would be a breaking change. """ short: Optional[str] = None @@ -306,6 +316,22 @@ class TrustScoreResult(_CamelModel): status: Optional[str] = Field(default=None, alias="trustStatus") threats: Optional[List[str]] = None scanned_at: Optional[str] = None + source: Optional[str] = None + created_at: Optional[str] = None + + @field_validator("created_at", mode="before") + @classmethod + def _coerce_epoch_millis(cls, value: Any) -> Any: + # This endpoint's createdAt is a raw epoch-milliseconds integer, not the + # {_seconds,_nanoseconds} Firestore shape the shared base-model validator + # handles — normalize it the same way (to an ISO-8601 string) here. + if isinstance(value, bool) or not isinstance(value, (int, float)): + return value + try: + dt = datetime.fromtimestamp(value / 1000, tz=timezone.utc) + return dt.isoformat().replace("+00:00", "Z") + except (OverflowError, OSError, ValueError): + return str(value) # --------------------------------------------------------------------------- @@ -314,12 +340,23 @@ class TrustScoreResult(_CamelModel): class NamespaceInfo(_CamelModel): - """Namespace info for the authenticated user.""" + """Namespace info for the authenticated user. + + ``upgrade_required`` is kept for backward compatibility — the platform + doesn't actually send it (an earlier version of this model guessed it did); + ``can_claim_custom_domain``/``can_claim_subdomain``/``namespace_data`` are + the real, platform-verified fields. ``namespace_data`` is a loose dict + (account-internal shape, e.g. ``userEmail``/``isActive``/``tier``/``userId``/ + ``claimedAt``) rather than its own typed model. + """ has_access: Optional[bool] = None namespace: Optional[str] = None tier: Optional[str] = None upgrade_required: Optional[bool] = None + can_claim_custom_domain: Optional[bool] = None + can_claim_subdomain: Optional[bool] = None + namespace_data: Optional[Dict[str, Any]] = None class NamespaceCheckResult(_CamelModel): @@ -430,16 +467,32 @@ class CustomDomain(_CamelModel): class AffiliateProgram(_CamelModel): - """An affiliate program.""" + """An affiliate program. + + Shared between the owned-program endpoints (create/list/get/update — the + fully-populated shape) and ``discover()`` (a public subset: no + ``merchant_id``/``max_partners``/``is_public``/timestamps) — every field is + ``Optional`` so both shapes validate against the same model. + + ``cookie_days`` is kept as a field name for backward compatibility even + though the wire key is ``cookieDurationDays``, not ``cookieDays`` (an + earlier version of this model guessed wrong). + """ id: Optional[str] = None + merchant_id: Optional[str] = None name: Optional[str] = None description: Optional[str] = None commission_type: Optional[str] = None cpc_rate: Optional[float] = None cpa_rate: Optional[float] = None - cookie_days: Optional[int] = None + cookie_days: Optional[int] = Field(default=None, alias="cookieDurationDays") + max_partners: Optional[int] = None + partner_count: Optional[int] = None status: Optional[str] = None + is_public: Optional[bool] = None + created_at: Optional[str] = None + updated_at: Optional[str] = None # --------------------------------------------------------------------------- @@ -606,6 +659,7 @@ class AggregateAnalytics(_CamelModel): period: Optional[str] = None total_clicks: Optional[int] = None unique_visitors: Optional[int] = None + bot_clicks_excluded: Optional[int] = None clicks_by_day: List[DayClicks] = Field(default_factory=list) country_breakdown: Dict[str, int] = Field(default_factory=dict) tier_limit: Optional[int] = None diff --git a/awsysco/resources/affiliate.py b/awsysco/resources/affiliate.py index d6549f9..a313512 100644 --- a/awsysco/resources/affiliate.py +++ b/awsysco/resources/affiliate.py @@ -37,7 +37,7 @@ def create_program( "description": "description", "cpc_rate": "cpcRate", "cpa_rate": "cpaRate", - "cookie_days": "cookieDays", + "cookie_days": "cookieDurationDays", } for k, v in kwargs.items(): mapped = field_map.get(k, k) @@ -84,7 +84,7 @@ def update_program(self, program_id: str, **kwargs: Any) -> AffiliateProgram: field_map = { "cpc_rate": "cpcRate", "cpa_rate": "cpaRate", - "cookie_days": "cookieDays", + "cookie_days": "cookieDurationDays", "commission_type": "commissionType", } for k, v in kwargs.items(): diff --git a/awsysco/resources/utm_templates.py b/awsysco/resources/utm_templates.py index 0c61a44..1b80bd0 100644 --- a/awsysco/resources/utm_templates.py +++ b/awsysco/resources/utm_templates.py @@ -20,8 +20,8 @@ def list(self) -> List[UtmTemplate]: Returns: A list of UtmTemplate objects. """ - resp = self._http.get("/api/v1/me") - items = resp.get("utmTemplates", []) if isinstance(resp, dict) else [] + resp = self._http.get("/api/user/utm-templates") + items = resp.get("templates", []) if isinstance(resp, dict) else [] return [UtmTemplate.model_validate(item) for item in items] def create( diff --git a/tests/contracts/sdk-contract.json b/tests/contracts/sdk-contract.json index 25fa765..7db9b2e 100644 --- a/tests/contracts/sdk-contract.json +++ b/tests/contracts/sdk-contract.json @@ -1,6 +1,6 @@ { "$schema": "awsys-sdk-contract/1", - "version": "1.0.8", + "version": "1.0.12", "platformBaseline": "2026-09", "baseUrl": "https://awsys.co", "auth": { @@ -297,18 +297,10 @@ "response": { "status": 200, "body": { - "shortCode": "abc123", + "shortCode": "3iWwb7", "fullPath": null, - "totalClicks": 1, - "clicks": [ - { - "timestamp": "2026-09-01T00:00:00.000Z", - "country": "MX", - "browser": "Chrome", - "os": "macOS", - "referrer": null - } - ] + "totalClicks": 0, + "clicks": [] } } }, @@ -328,15 +320,42 @@ "body": { "shortCode": "abc123", "fullPath": null, + "period": "7d", "totalClicks": 1, - "byCountry": { + "botClicksExcluded": 0, + "uniqueVisitors": 1, + "clicksByDay": [ + { + "date": "2026-09-01", + "clicks": 1 + } + ], + "countryBreakdown": { "MX": 1 }, - "byDay": { - "2026-09-01": 1 - } + "deviceBreakdown": { + "mobile": 0, + "desktop": 1, + "tablet": 0 + }, + "referrerBreakdown": {}, + "browserBreakdown": { + "Chrome": 1 + }, + "osBreakdown": { + "macOS": 1 + }, + "hourBreakdown": [ + { + "hour": 0, + "clicks": 1 + } + ], + "tierLimit": 90, + "tier": "builder" } - } + }, + "note": "real field names: clicksByDay[]/countryBreakdown{} etc. (apiV1.js aggregate); NOT byCountry/byDay" }, { "id": "bulk_create", @@ -409,206 +428,2084 @@ }, "utmTemplates": [ { - "id": "t1", - "name": "Launch", - "utmSource": "newsletter", - "utmMedium": "email", - "utmCampaign": "sept" - } - ] - } - } - }, - { - "id": "usage", - "capability": "10", - "request": { - "method": "GET", - "path": "/api/user/stats", - "query": {}, - "body": null - }, - "response": { - "status": 200, - "body": { - "totalLinks": 222, - "totalClicks": 5, - "linksCreatedThisMonth": 222, - "qrCodesThisMonth": 0, - "folderCount": 0, - "apiCallsThisMonth": 1, - "trackedClicksThisMonth": 5, - "tier": "pro", - "limits": { - "linksPerMonth": 1000, - "monthlyLinks": 1000, - "dailyLinks": 100, - "monthlyTrackedClicks": 10000, - "apiCallsPerMonth": 1000, - "qrCodes": 100, - "folders": 10, - "customSlugs": true - }, - "hasApiKey": true, - "apiKeyCreatedAt": "2026-09-07T03:09:24.886Z", - "userPrefix": "op0p", - "isPremium": true, - "overage": { - "active": false, - "startedAt": null, - "expiresAt": null, - "hoursUntilDrop": null, - "clicksThisCycle": 0, - "spendingLimitCents": null, - "estimatedChargeCents": 0 - } - } - }, - "note": "verified live 2026-09-07 on staging; limits values illustrative, keys exact" - }, - { - "id": "recent_clicks", - "capability": "11", - "request": { - "method": "GET", - "path": "/api/user/clicks/recent", - "query": { - "limit": "10" - }, - "body": null - }, - "response": { - "status": 200, - "body": { - "clicks": [ + "id": "t1", + "name": "Launch", + "utmSource": "newsletter", + "utmMedium": "email", + "utmCampaign": "sept" + } + ] + } + } + }, + { + "id": "usage", + "capability": "10", + "request": { + "method": "GET", + "path": "/api/user/stats", + "query": {}, + "body": null + }, + "response": { + "status": 200, + "body": { + "totalLinks": 388, + "totalClicks": 5, + "linksCreatedThisMonth": 388, + "qrCodesThisMonth": 0, + "folderCount": 0, + "apiCallsThisMonth": 324, + "trackedClicksThisMonth": 5, + "tier": "builder", + "limits": { + "linksPerMonth": "unlimited", + "monthlyLinks": "unlimited", + "dailyLinks": "unlimited", + "monthlyTrackedClicks": 500000, + "apiCallsPerMonth": 10000, + "qrCodes": "unlimited", + "folders": "unlimited", + "customSlugs": true + }, + "hasApiKey": true, + "apiKeyCreatedAt": "2026-09-08T14:02:18.541Z", + "userPrefix": "op0p", + "isPremium": true, + "overage": { + "active": false, + "startedAt": null, + "expiresAt": null, + "hoursUntilDrop": null, + "clicksThisCycle": 0, + "spendingLimitCents": 0, + "estimatedChargeCents": 0 + } + } + }, + "note": "verified live 2026-09-07 on staging; limits values illustrative, keys exact" + }, + { + "id": "recent_clicks", + "capability": "11", + "request": { + "method": "GET", + "path": "/api/user/clicks/recent", + "query": { + "limit": "10" + }, + "body": null + }, + "response": { + "status": 200, + "body": { + "clicks": [], + "count": 0 + } + }, + "note": "path is /api/user/clicks/recent (NOT /api/user/recent-clicks); requires features.liveGlobe on the account (403 FEATURE_DISABLED otherwise); verified live envelope {clicks,count}" + }, + { + "id": "profile_get", + "capability": "12", + "request": { + "method": "GET", + "path": "/api/user/profile", + "query": {}, + "body": null + }, + "response": { + "status": 200, + "body": { + "uid": "u1", + "email": "t@example.com", + "displayName": "T", + "subscriptionTier": "pro" + } + } + }, + { + "id": "profile_update", + "capability": "13", + "request": { + "method": "PATCH", + "path": "/api/user/profile", + "query": {}, + "body": { + "displayName": "New" + } + }, + "response": { + "status": 200, + "body": { + "success": true, + "displayName": "New" + } + }, + "note": "body keys are camelCase (user.js:212 reads displayName)" + }, + { + "id": "qr_url", + "capability": "14", + "request": { + "method": "GET", + "path": "/api/qr/abc123", + "query": { + "size": "300", + "color": "000000", + "bgColor": "ffffff" + }, + "body": null + }, + "response": { + "status": 200, + "body": {} + }, + "note": "client-side URL builder; assert exact URL string" + }, + { + "id": "qr_settings_get", + "capability": "15", + "request": { + "method": "GET", + "path": "/api/link/abc123/qr-settings", + "query": {}, + "body": null + }, + "response": { + "status": 200, + "body": { + "size": 300, + "color": "#000000", + "bgColor": "#ffffff", + "logo": null + } + } + }, + { + "id": "qr_settings_update", + "capability": "16", + "request": { + "method": "PUT", + "path": "/api/link/abc123/qr-settings", + "query": {}, + "body": { + "color": "#ff0000" + } + }, + "response": { + "status": 200, + "body": { + "success": true, + "color": "#ff0000" + } + } + }, + { + "id": "folders_list", + "capability": "17", + "request": { + "method": "GET", + "path": "/api/v1/folders", + "query": {}, + "body": null + }, + "response": { + "status": 200, + "body": { + "folders": [ + { + "id": "In8iGGFI6hbBBR2EIaBL", + "created": "2026-06-02T05:51:56.160Z", + "updated": "2026-06-02T05:51:56.160Z", + "name": "Bulk Organize Folder 1780379515", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "color": "#0077ff" + }, + { + "id": "uQhPmmYg9E5tfGbigx4m", + "updated": "2026-06-02T17:58:54.042Z", + "color": "#0077ff", + "name": "Bulk Organize Folder 1780423133", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T17:58:54.042Z", + "linkCount": 0 + }, + { + "id": "CXrMOf62jjFdi2YsyFoo", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T19:10:05.204Z", + "linkCount": 0, + "name": "Bulk Organize Folder 1780427405", + "created": "2026-06-02T19:10:05.204Z" + }, + { + "id": "HqHCqIZoRjmjLzkOVZrq", + "created": "2026-06-02T20:30:46.543Z", + "linkCount": 0, + "updated": "2026-06-02T20:30:46.543Z", + "color": "#0077ff", + "name": "Bulk Organize Folder 1780432246", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "Sa9shKdcv7arVlQreHvp", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1780709534", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-06T01:32:15.096Z", + "updated": "2026-06-06T01:32:15.096Z" + }, + { + "id": "qOCvLk9AJoUHEDYcZNzX", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1781131095", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-10T22:38:15.969Z", + "updated": "2026-06-10T22:38:15.969Z" + }, + { + "id": "eeZdk6TNjxYw9ZQU6FLm", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1782363825", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-25T05:03:46.107Z", + "updated": "2026-06-25T05:03:46.107Z" + }, + { + "id": "6NONXhTphLrdA8ScbRna", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1782864207", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-01T00:03:27.732Z", + "updated": "2026-07-01T00:03:27.732Z" + }, + { + "id": "vrth4JZNckCZvLpZPUYf", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1783132455", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-04T02:34:15.666Z", + "updated": "2026-07-04T02:34:15.666Z" + }, + { + "id": "GhK4pByujXcxAM0Ro4ik", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1783544054", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-08T20:54:14.664Z", + "updated": "2026-07-08T20:54:14.664Z" + }, + { + "id": "X2mn2fYtijMeUngjoLBO", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1783964876", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T17:47:56.340Z", + "updated": "2026-07-13T17:47:56.340Z" + }, + { + "id": "qeA80NFEI5gbuMv4jDTm", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1783968035", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T18:40:35.753Z", + "updated": "2026-07-13T18:40:35.753Z" + }, + { + "id": "qRaa1s99jmGxaIxCwk0q", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1783985607", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T23:33:27.387Z", + "updated": "2026-07-13T23:33:27.387Z" + }, + { + "id": "NWH52ohz57EFMjg4zvti", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1785954599", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-05T18:29:59.729Z", + "updated": "2026-08-05T18:29:59.729Z" + }, + { + "id": "tlIeKkeqLxPMOPeGdghT", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1787325799", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-21T15:23:19.649Z", + "updated": "2026-08-21T15:23:19.649Z" + }, + { + "id": "cnNqbUXu0m8SpJeAmBr3", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788032700", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-29T19:45:01.028Z", + "updated": "2026-08-29T19:45:01.028Z" + }, + { + "id": "SZtHivfP4NzipSsOMBQ5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788067237", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:20:37.232Z", + "updated": "2026-08-30T05:20:37.232Z" + }, + { + "id": "Cqfg94zO6nvaX4HL8OYK", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788068778", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:46:18.585Z", + "updated": "2026-08-30T05:46:18.585Z" + }, + { + "id": "xBda6oUYQlnGxYErzj95", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788074788", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:26:29.016Z", + "updated": "2026-08-30T07:26:29.016Z" + }, + { + "id": "irk0KgXSz0JK44z9pGAv", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788077312", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T08:08:32.775Z", + "updated": "2026-08-30T08:08:32.775Z" + }, + { + "id": "kDCe2vZfrt4xeIhC7Ar5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788077409", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T08:10:10.099Z", + "updated": "2026-08-30T08:10:10.099Z" + }, + { + "id": "xpqqwH8j3u5uuVFKp08B", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788100285", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:31:25.488Z", + "updated": "2026-08-30T14:31:25.488Z" + }, + { + "id": "AO8o5N2yrfCEJI7XNt7u", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788104551", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T15:42:31.767Z", + "updated": "2026-08-30T15:42:31.767Z" + }, + { + "id": "SS6UiNDRjrpekhrUOd7j", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "Bulk Organize Folder 1788820657", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-09-07T22:37:37.630Z", + "updated": "2026-09-07T22:37:37.630Z" + }, + { + "id": "asHvGvS7Q1K6syobzYqC", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "contract-test-1788097754", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T13:49:14.590Z", + "updated": "2026-08-30T13:49:14.591Z" + }, + { + "id": "g0Dbx7JWym1dSIKYzTDS", + "color": "#0077ff", + "name": "e2e-assign-folder-1780378481", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "created": "2026-06-02T05:34:41.578Z", + "updated": "2026-06-02T05:34:41.578Z" + }, + { + "id": "i9FphVvC3SGSKt74aUdi", + "updated": "2026-06-02T16:58:05.313Z", + "color": "#0077ff", + "name": "e2e-assign-folder-1780419485", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T16:58:05.313Z", + "linkCount": 1 + }, + { + "id": "wE2rsFwEVziECFeyMMqS", + "color": "#0077ff", + "updated": "2026-06-02T17:41:42.036Z", + "name": "e2e-assign-folder-1780422101", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T17:41:42.036Z", + "linkCount": 1 + }, + { + "id": "UoZIXrUvD8tVo5fwQ2tj", + "updated": "2026-06-02T18:26:58.671Z", + "created": "2026-06-02T18:26:58.671Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "name": "e2e-assign-folder-1780424818", + "linkCount": 1 + }, + { + "id": "yKfGaqJMYbynbRk8EQ9f", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T18:31:01.304Z", + "created": "2026-06-02T18:31:01.304Z", + "color": "#0077ff", + "name": "e2e-assign-folder-1780425061", + "linkCount": 1 + }, + { + "id": "LfQKKEYqkoNqvJ7fwB67", + "color": "#0077ff", + "name": "e2e-assign-folder-1780425334", + "created": "2026-06-02T18:35:34.616Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T18:35:34.617Z", + "linkCount": 1 + }, + { + "id": "1YyIyoxu0LPg9FVExIQE", + "created": "2026-06-02T20:05:21.973Z", + "name": "e2e-assign-folder-1780430721", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T20:05:21.973Z", + "color": "#0077ff", + "linkCount": 1 + }, + { + "id": "EJlKg2kotkJ9hi69oxhD", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1780705164", + "color": "#0077ff", + "created": "2026-06-06T00:19:24.935Z", + "updated": "2026-06-06T00:19:24.935Z", + "linkCount": 1 + }, + { + "id": "7WRuHRRD5l3ONrunXRnj", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1781129163", + "color": "#0077ff", + "created": "2026-06-10T22:06:03.986Z", + "updated": "2026-06-10T22:06:03.986Z", + "linkCount": 1 + }, + { + "id": "7675NRS7cn4QRqxPG16i", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1782362867", + "color": "#0077ff", + "created": "2026-06-25T04:47:48.209Z", + "updated": "2026-06-25T04:47:48.209Z", + "linkCount": 1 + }, + { + "id": "6AzKWtODsZJFksSaKqGy", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1782863222", + "color": "#0077ff", + "created": "2026-06-30T23:47:02.434Z", + "updated": "2026-06-30T23:47:02.434Z", + "linkCount": 1 + }, + { + "id": "0hiUBvAdijeMJetItDKd", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1783131460", + "color": "#0077ff", + "created": "2026-07-04T02:17:40.444Z", + "updated": "2026-07-04T02:17:40.444Z", + "linkCount": 1 + }, + { + "id": "8zihJTrt91hO6BngtgTn", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1783543070", + "color": "#0077ff", + "created": "2026-07-08T20:37:50.507Z", + "updated": "2026-07-08T20:37:50.507Z", + "linkCount": 1 + }, + { + "id": "TtWZKGprrD6rqgyhsYos", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1783963679", + "color": "#0077ff", + "created": "2026-07-13T17:27:59.830Z", + "updated": "2026-07-13T17:27:59.830Z", + "linkCount": 1 + }, + { + "id": "9AtufDIVtTTg6OXoBiXS", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1783966776", + "color": "#0077ff", + "created": "2026-07-13T18:19:36.719Z", + "updated": "2026-07-13T18:19:36.719Z", + "linkCount": 1 + }, + { + "id": "Uacf8kH479JSC7wyb4kH", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1783984419", + "color": "#0077ff", + "created": "2026-07-13T23:13:40.218Z", + "updated": "2026-07-13T23:13:40.218Z", + "linkCount": 1 + }, + { + "id": "vsXBvfw2Lz8tkZmPv8Fw", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1785952611", + "color": "#0077ff", + "created": "2026-08-05T17:56:51.301Z", + "updated": "2026-08-05T17:56:51.301Z", + "linkCount": 1 + }, + { + "id": "Yt37kjVu1k6UzC3i93yX", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1787324641", + "color": "#0077ff", + "created": "2026-08-21T15:04:01.404Z", + "updated": "2026-08-21T15:04:01.404Z", + "linkCount": 1 + }, + { + "id": "leLpo7BTygL8bgFT7xXe", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788031701", + "color": "#0077ff", + "created": "2026-08-29T19:28:22.011Z", + "updated": "2026-08-29T19:28:22.011Z", + "linkCount": 1 + }, + { + "id": "sA2mQrlKUVK8ZKKroIIZ", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788066049", + "color": "#0077ff", + "created": "2026-08-30T05:00:49.733Z", + "updated": "2026-08-30T05:00:49.733Z", + "linkCount": 1 + }, + { + "id": "vOPbZfdg3RQBsV36FNRM", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788067639", + "color": "#0077ff", + "created": "2026-08-30T05:27:19.711Z", + "updated": "2026-08-30T05:27:19.711Z", + "linkCount": 1 + }, + { + "id": "7vAVkdfV5cN0kQb6Mt7N", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788073711", + "color": "#0077ff", + "created": "2026-08-30T07:08:31.773Z", + "updated": "2026-08-30T07:08:31.774Z", + "linkCount": 1 + }, + { + "id": "2CTx2wbfMza2bgYZK61I", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788076144", + "color": "#0077ff", + "created": "2026-08-30T07:49:04.940Z", + "updated": "2026-08-30T07:49:04.940Z", + "linkCount": 1 + }, + { + "id": "8NhICMP5bUMGw6wgNPQ0", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788099187", + "color": "#0077ff", + "created": "2026-08-30T14:13:07.666Z", + "updated": "2026-08-30T14:13:07.666Z", + "linkCount": 1 + }, + { + "id": "9FFrvyOOS4Y5NpWc1yMS", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788101057", + "color": "#0077ff", + "created": "2026-08-30T14:44:18.206Z", + "updated": "2026-08-30T14:44:18.206Z", + "linkCount": 1 + }, + { + "id": "6zzyWAqxz1IQmOprnsgB", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-assign-folder-1788819652", + "color": "#0077ff", + "created": "2026-09-07T22:20:52.785Z", + "updated": "2026-09-07T22:20:52.785Z", + "linkCount": 1 + }, + { + "id": "1DDKItGUW9YfMXe8ipCc", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T05:34:41.842Z", + "color": "#0077ff", + "updated": "2026-06-02T05:34:41.842Z", + "linkCount": 0, + "name": "e2e-move-a-1780378481" + }, + { + "id": "UwRqTcTiaJUeX0OH3OFi", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "name": "e2e-move-a-1780419485", + "updated": "2026-06-02T16:58:06.038Z", + "created": "2026-06-02T16:58:06.038Z", + "linkCount": 0 + }, + { + "id": "QHulmruZfEGRnlLEuekr", + "created": "2026-06-02T17:41:42.558Z", + "name": "e2e-move-a-1780422102", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T17:41:42.558Z", + "color": "#0077ff", + "linkCount": 0 + }, + { + "id": "KVUJbvdAzrq3Iwzg3C3n", + "updated": "2026-06-02T18:26:59.175Z", + "color": "#0077ff", + "created": "2026-06-02T18:26:59.175Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1780424819", + "linkCount": 0 + }, + { + "id": "30Skv0C7ORrXIPEASO8v", + "created": "2026-06-02T18:31:01.780Z", + "updated": "2026-06-02T18:31:01.780Z", + "color": "#0077ff", + "name": "e2e-move-a-1780425061", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0 + }, + { + "id": "P4b8JydFmpdLfB8nQnLy", + "created": "2026-06-02T18:35:35.288Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1780425335", + "updated": "2026-06-02T18:35:35.288Z", + "color": "#0077ff", + "linkCount": 0 + }, + { + "id": "CvLQZFuPZB7y1rgNb4Gf", + "color": "#0077ff", + "updated": "2026-06-02T20:05:22.496Z", + "name": "e2e-move-a-1780430722", + "created": "2026-06-02T20:05:22.496Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0 + }, + { + "id": "6JCReLVOb5Pvw3wPrfhV", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1780705165", + "color": "#0077ff", + "created": "2026-06-06T00:19:25.435Z", + "updated": "2026-06-06T00:19:25.435Z", + "linkCount": 0 + }, + { + "id": "7UT4FFIWaZLi4e8U5KUp", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1781129164", + "color": "#0077ff", + "created": "2026-06-10T22:06:04.615Z", + "updated": "2026-06-10T22:06:04.615Z", + "linkCount": 0 + }, + { + "id": "qIQMnCnljiqSYQPbYYeG", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1782362868", + "color": "#0077ff", + "created": "2026-06-25T04:47:48.735Z", + "updated": "2026-06-25T04:47:48.735Z", + "linkCount": 0 + }, + { + "id": "LVx3ilQzUmEzq4x6n17C", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1782863222", + "color": "#0077ff", + "created": "2026-06-30T23:47:03.003Z", + "updated": "2026-06-30T23:47:03.003Z", + "linkCount": 0 + }, + { + "id": "0pBcolS5Wpo5wxmaJzV4", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1783131460", + "color": "#0077ff", + "created": "2026-07-04T02:17:40.965Z", + "updated": "2026-07-04T02:17:40.965Z", + "linkCount": 0 + }, + { + "id": "wpAJFi1iUspMvdd51fzC", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1783543070", + "color": "#0077ff", + "created": "2026-07-08T20:37:50.927Z", + "updated": "2026-07-08T20:37:50.927Z", + "linkCount": 0 + }, + { + "id": "ZFWn3ThzMHRK3Q91arA6", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1783963680", + "color": "#0077ff", + "created": "2026-07-13T17:28:00.611Z", + "updated": "2026-07-13T17:28:00.611Z", + "linkCount": 0 + }, + { + "id": "SMGZAgVAwySPNFW9zPky", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1783966777", + "color": "#0077ff", + "created": "2026-07-13T18:19:37.304Z", + "updated": "2026-07-13T18:19:37.304Z", + "linkCount": 0 + }, + { + "id": "cMUv6cM8Oqf2kzPDWLPx", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1783984420", + "color": "#0077ff", + "created": "2026-07-13T23:13:40.721Z", + "updated": "2026-07-13T23:13:40.721Z", + "linkCount": 0 + }, + { + "id": "NXWzQSD6a8XgLwOBCk8t", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1785952611", + "color": "#0077ff", + "created": "2026-08-05T17:56:51.775Z", + "updated": "2026-08-05T17:56:51.775Z", + "linkCount": 0 + }, + { + "id": "wqoi0ihfsYvCt5Zqjg1B", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1787324641", + "color": "#0077ff", + "created": "2026-08-21T15:04:01.890Z", + "updated": "2026-08-21T15:04:01.890Z", + "linkCount": 0 + }, + { + "id": "cDF9Spt1QRkWlSlKs3b9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788031702", + "color": "#0077ff", + "created": "2026-08-29T19:28:22.751Z", + "updated": "2026-08-29T19:28:22.751Z", + "linkCount": 0 + }, + { + "id": "Q7cNHQLjIsQFW7tRLV0L", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788066049", + "color": "#0077ff", + "created": "2026-08-30T05:00:50.176Z", + "updated": "2026-08-30T05:00:50.176Z", + "linkCount": 0 + }, + { + "id": "iMxjFRY2na1ct8cRgkf5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788067639", + "color": "#0077ff", + "created": "2026-08-30T05:27:20.175Z", + "updated": "2026-08-30T05:27:20.175Z", + "linkCount": 0 + }, + { + "id": "IzCWRB1FDKDaKvb7fbnF", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788073711", + "color": "#0077ff", + "created": "2026-08-30T07:08:32.277Z", + "updated": "2026-08-30T07:08:32.277Z", + "linkCount": 0 + }, + { + "id": "Y4l3wV94TVqDQsnuqo6x", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788076145", + "color": "#0077ff", + "created": "2026-08-30T07:49:05.516Z", + "updated": "2026-08-30T07:49:05.516Z", + "linkCount": 0 + }, + { + "id": "MzNT100Ygigujvsb4kar", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788099187", + "color": "#0077ff", + "created": "2026-08-30T14:13:08.163Z", + "updated": "2026-08-30T14:13:08.163Z", + "linkCount": 0 + }, + { + "id": "rz5CPikvZxpHfgFdoifo", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788101058", + "color": "#0077ff", + "created": "2026-08-30T14:44:18.694Z", + "updated": "2026-08-30T14:44:18.694Z", + "linkCount": 0 + }, + { + "id": "aHr5xohz2bU4A2TIQiU5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-a-1788819652", + "color": "#0077ff", + "created": "2026-09-07T22:20:53.243Z", + "updated": "2026-09-07T22:20:53.243Z", + "linkCount": 0 + }, + { + "id": "t2SuQtsjHn2sYRD4s8R4", + "updated": "2026-06-02T05:34:42.113Z", + "linkCount": 0, + "color": "#0077ff", + "created": "2026-06-02T05:34:42.113Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1780378481" + }, + { + "id": "Anc5ACAfAb6lRDJ1Bavj", + "updated": "2026-06-02T16:58:06.348Z", + "name": "e2e-move-b-1780419486", + "created": "2026-06-02T16:58:06.348Z", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 1 + }, + { + "id": "866zpQl78qhWZBveONCe", + "updated": "2026-06-02T17:41:42.811Z", + "color": "#0077ff", + "name": "e2e-move-b-1780422102", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T17:41:42.811Z", + "linkCount": 1 + }, + { + "id": "5YXbqBjlb5vcjWEYUGqA", + "created": "2026-06-02T18:26:59.437Z", + "name": "e2e-move-b-1780424819", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T18:26:59.437Z", + "color": "#0077ff", + "linkCount": 1 + }, + { + "id": "Rx7laEBLVfdHZqpfMtfY", + "updated": "2026-06-02T18:31:02.031Z", + "created": "2026-06-02T18:31:02.031Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "name": "e2e-move-b-1780425061", + "linkCount": 1 + }, + { + "id": "9QH3lffLyp0pkw90dml6", + "updated": "2026-06-02T18:35:35.518Z", + "color": "#0077ff", + "name": "e2e-move-b-1780425335", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T18:35:35.518Z", + "linkCount": 1 + }, + { + "id": "ef9psNhpmRhpSYHpFxe7", + "color": "#0077ff", + "name": "e2e-move-b-1780430722", + "created": "2026-06-02T20:05:22.768Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T20:05:22.768Z", + "linkCount": 1 + }, + { + "id": "YAFdOvo0evo7EIKMXw7W", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1780705165", + "color": "#0077ff", + "created": "2026-06-06T00:19:25.688Z", + "updated": "2026-06-06T00:19:25.688Z", + "linkCount": 1 + }, + { + "id": "u1bGzBgmLtLKyGt2JZHq", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1781129164", + "color": "#0077ff", + "created": "2026-06-10T22:06:04.853Z", + "updated": "2026-06-10T22:06:04.853Z", + "linkCount": 1 + }, + { + "id": "57o1fcevfh9Xm6iYGL2L", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1782362868", + "color": "#0077ff", + "created": "2026-06-25T04:47:48.983Z", + "updated": "2026-06-25T04:47:48.983Z", + "linkCount": 1 + }, + { + "id": "Bv7YMOxFcHZg2sT2h1J5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1782863222", + "color": "#0077ff", + "created": "2026-06-30T23:47:03.302Z", + "updated": "2026-06-30T23:47:03.302Z", + "linkCount": 1 + }, + { + "id": "lxdLbdScdRmTJzbAzvJm", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1783131460", + "color": "#0077ff", + "created": "2026-07-04T02:17:41.224Z", + "updated": "2026-07-04T02:17:41.224Z", + "linkCount": 1 + }, + { + "id": "3eBNvtbjJICLibrIL1U9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1783543071", + "color": "#0077ff", + "created": "2026-07-08T20:37:51.382Z", + "updated": "2026-07-08T20:37:51.382Z", + "linkCount": 1 + }, + { + "id": "hMAC3yGZqH21XJwnDoA4", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1783963680", + "color": "#0077ff", + "created": "2026-07-13T17:28:00.889Z", + "updated": "2026-07-13T17:28:00.889Z", + "linkCount": 1 + }, + { + "id": "He3I67Tr9d604wleeHkk", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1783966777", + "color": "#0077ff", + "created": "2026-07-13T18:19:37.591Z", + "updated": "2026-07-13T18:19:37.591Z", + "linkCount": 1 + }, + { + "id": "LVtkOpIEncmsjOfy9jaY", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1783984420", + "color": "#0077ff", + "created": "2026-07-13T23:13:41.003Z", + "updated": "2026-07-13T23:13:41.003Z", + "linkCount": 1 + }, + { + "id": "3Za0NbbRaqdO1qOjVN1F", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1785952611", + "color": "#0077ff", + "created": "2026-08-05T17:56:52.109Z", + "updated": "2026-08-05T17:56:52.109Z", + "linkCount": 1 + }, + { + "id": "W60D1CxaBnxWUyNEjXSB", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1787324641", + "color": "#0077ff", + "created": "2026-08-21T15:04:02.131Z", + "updated": "2026-08-21T15:04:02.131Z", + "linkCount": 1 + }, + { + "id": "WfDvzi4PLVBnLnnI59w5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788031702", + "color": "#0077ff", + "created": "2026-08-29T19:28:23.009Z", + "updated": "2026-08-29T19:28:23.009Z", + "linkCount": 1 + }, + { + "id": "xmJl8pb4t8AE4t7GGLmf", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788066050", + "color": "#0077ff", + "created": "2026-08-30T05:00:50.392Z", + "updated": "2026-08-30T05:00:50.392Z", + "linkCount": 1 + }, + { + "id": "LB1DBNJRylxW6I5CqF3l", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788067640", + "color": "#0077ff", + "created": "2026-08-30T05:27:20.430Z", + "updated": "2026-08-30T05:27:20.430Z", + "linkCount": 1 + }, + { + "id": "l4mJMgcy38NHKlP6sN53", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788073712", + "color": "#0077ff", + "created": "2026-08-30T07:08:32.531Z", + "updated": "2026-08-30T07:08:32.531Z", + "linkCount": 1 + }, + { + "id": "T2YDRP2dAdDNK8CtVWv8", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788076145", + "color": "#0077ff", + "created": "2026-08-30T07:49:05.857Z", + "updated": "2026-08-30T07:49:05.857Z", + "linkCount": 1 + }, + { + "id": "wxWH32irTuPMxg7TzK8W", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788099188", + "color": "#0077ff", + "created": "2026-08-30T14:13:08.391Z", + "updated": "2026-08-30T14:13:08.391Z", + "linkCount": 1 + }, + { + "id": "u4aGVVvy9IOX5L8UtgJO", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788101058", + "color": "#0077ff", + "created": "2026-08-30T14:44:18.962Z", + "updated": "2026-08-30T14:44:18.962Z", + "linkCount": 1 + }, + { + "id": "m15KutJVQWfMAsE76nGQ", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-move-b-1788819653", + "color": "#0077ff", + "created": "2026-09-07T22:20:53.487Z", + "updated": "2026-09-07T22:20:53.487Z", + "linkCount": 1 + }, + { + "id": "4tSyG0LDr2j1eKAl6qud", + "updated": "2026-06-02T05:34:37.467Z", + "color": "#0077ff", + "linkCount": 0, + "name": "e2e-pro-folder-1780378477", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T05:34:37.467Z" + }, + { + "id": "oV7Qkyj2fktDgWxJj7bM", + "created": "2026-06-02T16:58:01.164Z", + "updated": "2026-06-02T16:58:01.164Z", + "name": "e2e-pro-folder-1780419480", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0 + }, + { + "id": "7uTUZoqvJfyceEV3tZj2", + "updated": "2026-06-02T17:41:38.254Z", + "created": "2026-06-02T17:41:38.254Z", + "linkCount": 0, + "name": "e2e-pro-folder-1780422098", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "DovPoVMKOd8MtFqQ3a4c", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T18:26:54.805Z", + "created": "2026-06-02T18:26:54.805Z", + "linkCount": 0, + "name": "e2e-pro-folder-1780424814", + "color": "#0077ff" + }, + { + "id": "aQKDGRSVbAbjYjJcDU8N", + "color": "#0077ff", + "updated": "2026-06-02T18:30:57.485Z", + "linkCount": 0, + "name": "e2e-pro-folder-1780425057", + "created": "2026-06-02T18:30:57.485Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "eV5uBKHvK9hFJ9RrhcxH", + "created": "2026-06-02T18:35:30.056Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "color": "#0077ff", + "name": "e2e-pro-folder-1780425329", + "updated": "2026-06-02T18:35:30.056Z" + }, + { + "id": "Z236S6toVxlgLuAjQ1cV", + "updated": "2026-06-02T20:05:17.716Z", + "color": "#0077ff", + "name": "e2e-pro-folder-1780430717", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T20:05:17.716Z", + "linkCount": 0 + }, + { + "id": "b7B85hjOpuJT7TRfPlRj", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1780705160", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-06T00:19:20.817Z", + "updated": "2026-06-06T00:19:20.817Z" + }, + { + "id": "x9tuXFat24IJVdQwtN7S", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1781129159", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-10T22:05:59.670Z", + "updated": "2026-06-10T22:05:59.670Z" + }, + { + "id": "SVJ2BxPHdBDkX1s6ZtPz", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1782362864", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-25T04:47:44.438Z", + "updated": "2026-06-25T04:47:44.438Z" + }, + { + "id": "CFE4u8HLSKtISBnX7Wwd", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1782863217", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-30T23:46:58.273Z", + "updated": "2026-06-30T23:46:58.273Z" + }, + { + "id": "I4Cu4BmGeTBa7QyNJ07h", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1783131455", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-04T02:17:36.169Z", + "updated": "2026-07-04T02:17:36.169Z" + }, + { + "id": "AisqXITiUFnDkDdYloyS", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1783543066", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-08T20:37:46.938Z", + "updated": "2026-07-08T20:37:46.938Z" + }, + { + "id": "YOBmmohAVOfcP066jAN6", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1783963674", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T17:27:54.998Z", + "updated": "2026-07-13T17:27:54.998Z" + }, + { + "id": "s1XkSAvrd4H6Wo4GP6Or", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1783966771", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T18:19:31.811Z", + "updated": "2026-07-13T18:19:31.811Z" + }, + { + "id": "5tlV1Qp0L75hYVWhXz8W", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1783984415", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T23:13:35.784Z", + "updated": "2026-07-13T23:13:35.784Z" + }, + { + "id": "46UQLZ0jFfAZOoo6OwrW", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1785952606", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-05T17:56:47.148Z", + "updated": "2026-08-05T17:56:47.148Z" + }, + { + "id": "SMsSalwzDzDCzgePI4lU", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1787324636", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-21T15:03:56.898Z", + "updated": "2026-08-21T15:03:56.898Z" + }, + { + "id": "GiGUKbouJw2DUw3QxUXo", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788031697", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-29T19:28:17.617Z", + "updated": "2026-08-29T19:28:17.617Z" + }, + { + "id": "7856QVKqIfjUIbS7a4XN", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788066045", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:00:45.666Z", + "updated": "2026-08-30T05:00:45.666Z" + }, + { + "id": "fEWEF6ygUy7O8K9A3uEl", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788067635", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:27:15.770Z", + "updated": "2026-08-30T05:27:15.770Z" + }, + { + "id": "N9AwFj79nLyvu06qEA1k", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788073707", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:08:27.636Z", + "updated": "2026-08-30T07:08:27.636Z" + }, + { + "id": "zxZQtaaPQK8v6Q3WzcR8", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788076140", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:49:00.766Z", + "updated": "2026-08-30T07:49:00.766Z" + }, + { + "id": "bT7WgrY9Lqddm0e7wW0R", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788099183", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:13:03.381Z", + "updated": "2026-08-30T14:13:03.381Z" + }, + { + "id": "rzgrq5yfh2wb45PXF41l", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788101053", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:44:13.896Z", + "updated": "2026-08-30T14:44:13.896Z" + }, + { + "id": "14XXJdZOFDU6UNvTdagU", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-pro-folder-1788819648", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-09-07T22:20:48.633Z", + "updated": "2026-09-07T22:20:48.633Z" + }, + { + "id": "jQfbsYw5GK52LLRlwSYF", + "created": "2026-06-02T05:34:42.940Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "name": "e2e-remove-folder-1780378482", + "color": "#0077ff", + "updated": "2026-06-02T05:34:42.940Z" + }, + { + "id": "vAlTHAvkZ6vW5RWIh6XK", + "updated": "2026-06-02T16:58:08.581Z", + "color": "#0077ff", + "name": "e2e-remove-folder-1780419488", + "created": "2026-06-02T16:58:08.581Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 1 + }, + { + "id": "szFvv0jWIv6sZjr6fmye", + "updated": "2026-06-02T17:41:44.530Z", + "created": "2026-06-02T17:41:44.530Z", + "name": "e2e-remove-folder-1780422104", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 1 + }, + { + "id": "4PKjysOEQBAlxiC2hNY7", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "name": "e2e-remove-folder-1780424821", + "updated": "2026-06-02T18:27:01.369Z", + "created": "2026-06-02T18:27:01.369Z", + "linkCount": 1 + }, + { + "id": "LYngQQgJSapbYqPjBI2U", + "color": "#0077ff", + "name": "e2e-remove-folder-1780425064", + "created": "2026-06-02T18:31:04.183Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T18:31:04.183Z", + "linkCount": 1 + }, + { + "id": "0xNsJGZKQ0ibgjcWSMkm", + "updated": "2026-06-02T18:35:37.183Z", + "created": "2026-06-02T18:35:37.183Z", + "name": "e2e-remove-folder-1780425337", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 1 + }, + { + "id": "R2EnZhaIEwQY1FsfL0px", + "created": "2026-06-02T20:05:25.003Z", + "updated": "2026-06-02T20:05:25.003Z", + "color": "#0077ff", + "name": "e2e-remove-folder-1780430724", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 1 + }, + { + "id": "ht8wh72ObS19WWS7jvqr", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1780705167", + "color": "#0077ff", + "created": "2026-06-06T00:19:27.488Z", + "updated": "2026-06-06T00:19:27.488Z", + "linkCount": 1 + }, + { + "id": "RKKRSI20IRNAytMhG9oz", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1781129166", + "color": "#0077ff", + "created": "2026-06-10T22:06:07.153Z", + "updated": "2026-06-10T22:06:07.153Z", + "linkCount": 1 + }, + { + "id": "DoJ5GzUAm2kVEUvKCLLu", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1782362870", + "color": "#0077ff", + "created": "2026-06-25T04:47:50.680Z", + "updated": "2026-06-25T04:47:50.680Z", + "linkCount": 1 + }, + { + "id": "DvzrbUlDWqDVgy8hR8Pt", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1782863225", + "color": "#0077ff", + "created": "2026-06-30T23:47:05.425Z", + "updated": "2026-06-30T23:47:05.425Z", + "linkCount": 1 + }, + { + "id": "4hAZGrswNfev05pEzWg9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1783131462", + "color": "#0077ff", + "created": "2026-07-04T02:17:43.066Z", + "updated": "2026-07-04T02:17:43.066Z", + "linkCount": 1 + }, + { + "id": "s1JeoEkMWFSBuFBNLFiK", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1783543072", + "color": "#0077ff", + "created": "2026-07-08T20:37:52.830Z", + "updated": "2026-07-08T20:37:52.830Z", + "linkCount": 1 + }, + { + "id": "HHiHynD9mlqXaYOM1wmZ", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1783963682", + "color": "#0077ff", + "created": "2026-07-13T17:28:02.993Z", + "updated": "2026-07-13T17:28:02.993Z", + "linkCount": 1 + }, + { + "id": "B0h3yAeReoTUB7282dNW", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1783966779", + "color": "#0077ff", + "created": "2026-07-13T18:19:39.612Z", + "updated": "2026-07-13T18:19:39.612Z", + "linkCount": 1 + }, + { + "id": "mErDyv2e5kKA1rjzv3gX", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1783984422", + "color": "#0077ff", + "created": "2026-07-13T23:13:42.690Z", + "updated": "2026-07-13T23:13:42.690Z", + "linkCount": 1 + }, + { + "id": "EHY6hOuZwRkrwOiP5g8g", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1785952614", + "color": "#0077ff", + "created": "2026-08-05T17:56:54.301Z", + "updated": "2026-08-05T17:56:54.301Z", + "linkCount": 1 + }, + { + "id": "1PIC5KoUfLhfAmx2uVB5", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1787324643", + "color": "#0077ff", + "created": "2026-08-21T15:04:03.873Z", + "updated": "2026-08-21T15:04:03.873Z", + "linkCount": 1 + }, + { + "id": "v4f9dTnbTMGx4ZLBeL0D", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788031704", + "color": "#0077ff", + "created": "2026-08-29T19:28:24.930Z", + "updated": "2026-08-29T19:28:24.930Z", + "linkCount": 1 + }, + { + "id": "xaplCXMFCJkyUJFGlGfH", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788066051", + "color": "#0077ff", + "created": "2026-08-30T05:00:51.921Z", + "updated": "2026-08-30T05:00:51.921Z", + "linkCount": 1 + }, + { + "id": "bs2Crl2QUi6CbYLU4HbC", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788067641", + "color": "#0077ff", + "created": "2026-08-30T05:27:22.342Z", + "updated": "2026-08-30T05:27:22.342Z", + "linkCount": 1 + }, + { + "id": "3rWRfypVeAstiQ9rYh1Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788073713", + "color": "#0077ff", + "created": "2026-08-30T07:08:34.157Z", + "updated": "2026-08-30T07:08:34.157Z", + "linkCount": 1 + }, + { + "id": "ExKGQpApPdUAMeQu9fWu", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788076147", + "color": "#0077ff", + "created": "2026-08-30T07:49:07.557Z", + "updated": "2026-08-30T07:49:07.557Z", + "linkCount": 1 + }, + { + "id": "hoWILwvhVgQ2zME7Lf3Y", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788099189", + "color": "#0077ff", + "created": "2026-08-30T14:13:10.058Z", + "updated": "2026-08-30T14:13:10.058Z", + "linkCount": 1 + }, + { + "id": "upJaLcd6kHTLT0pl3Ka9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788101060", + "color": "#0077ff", + "created": "2026-08-30T14:44:20.614Z", + "updated": "2026-08-30T14:44:20.614Z", + "linkCount": 1 + }, + { + "id": "FURC7rnZJNw5gIYIKkWI", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-remove-folder-1788819654", + "color": "#0077ff", + "created": "2026-09-07T22:20:55.119Z", + "updated": "2026-09-07T22:20:55.119Z", + "linkCount": 1 + }, + { + "id": "4ng3U4Nod20Og6xYKqC0", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "created": "2026-06-02T05:34:38.234Z", + "name": "e2e-renamed-1780378478", + "updated": "2026-06-02T05:34:38.477Z" + }, + { + "id": "DnvoAL72Bg0smIlzllYc", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "created": "2026-06-02T16:58:02.026Z", + "linkCount": 0, + "name": "e2e-renamed-1780419482", + "updated": "2026-06-02T16:58:02.317Z" + }, + { + "id": "o2bD3CqF6FvSrdZ1wj4j", + "color": "#0077ff", + "linkCount": 0, + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "created": "2026-06-02T17:41:39.019Z", + "name": "e2e-renamed-1780422099", + "updated": "2026-06-02T17:41:39.328Z" + }, + { + "id": "EDRPqSepIY2Kw4oA31hE", + "created": "2026-06-02T18:26:55.544Z", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "name": "e2e-renamed-1780424815", + "updated": "2026-06-02T18:26:55.812Z" + }, + { + "id": "fr5pj0tgrBJckWCX4vKi", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-02T18:30:58.233Z", + "name": "e2e-renamed-1780425058", + "updated": "2026-06-02T18:30:58.499Z" + }, + { + "id": "QrWYrczr8COrn58ukSKV", + "color": "#0077ff", + "created": "2026-06-02T18:35:31.080Z", + "linkCount": 0, + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "e2e-renamed-1780425331", + "updated": "2026-06-02T18:35:31.348Z" + }, + { + "id": "DeH9NNMOwkS5LoUxkT4U", + "created": "2026-06-02T20:05:18.740Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0, + "color": "#0077ff", + "name": "e2e-renamed-1780430718", + "updated": "2026-06-02T20:05:19.012Z" + }, + { + "id": "rWKG7LuobSzqfg2tDkrU", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-06T00:19:21.754Z", + "name": "e2e-renamed-1780705161", + "updated": "2026-06-06T00:19:22.026Z" + }, + { + "id": "11qtLwOYMwaRyJxt4jXS", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-10T22:06:00.820Z", + "name": "e2e-renamed-1781129160", + "updated": "2026-06-10T22:06:01.093Z" + }, + { + "id": "Lx4KKNrmwFLOTZoBsC6Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-25T04:47:45.355Z", + "name": "e2e-renamed-1782362865", + "updated": "2026-06-25T04:47:45.614Z" + }, + { + "id": "pq0QJgJDLng7izqXciGN", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-30T23:46:59.204Z", + "name": "e2e-renamed-1782863219", + "updated": "2026-06-30T23:46:59.497Z" + }, + { + "id": "H68EFC04ZiAlJAt1XmHW", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-04T02:17:37.233Z", + "name": "e2e-renamed-1783131457", + "updated": "2026-07-04T02:17:37.552Z" + }, + { + "id": "y6kStOyZqjIjUt2pGX9m", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-08T20:37:47.831Z", + "name": "e2e-renamed-1783543067", + "updated": "2026-07-08T20:37:48.062Z" + }, + { + "id": "ZqlKfQr7DIxE3UenyaVM", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T17:27:56.196Z", + "name": "e2e-renamed-1783963676", + "updated": "2026-07-13T17:27:56.543Z" + }, + { + "id": "cRgVFuRshf8bE7KrQpDH", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T18:19:32.972Z", + "name": "e2e-renamed-1783966772", + "updated": "2026-07-13T18:19:33.294Z" + }, + { + "id": "llaA3XuQ8YDO0YFsNEi9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T23:13:36.738Z", + "name": "e2e-renamed-1783984416", + "updated": "2026-07-13T23:13:37.124Z" + }, + { + "id": "lmzcbM3MzRderu3QwcQ4", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-05T17:56:48.194Z", + "name": "e2e-renamed-1785952608", + "updated": "2026-08-05T17:56:48.466Z" + }, + { + "id": "NkcuFlceLzcfZtmBQYmM", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-21T15:03:57.877Z", + "name": "e2e-renamed-1787324637", + "updated": "2026-08-21T15:03:58.310Z" + }, + { + "id": "ewFzHYTKge28d6bsW81R", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-29T19:28:18.674Z", + "name": "e2e-renamed-1788031698", + "updated": "2026-08-29T19:28:18.941Z" + }, + { + "id": "f7V3gZJyOjAZg3oF2Qo8", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:00:46.578Z", + "name": "e2e-renamed-1788066046", + "updated": "2026-08-30T05:00:46.842Z" + }, + { + "id": "8gP41oQG5s6PPT3ys0sZ", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:27:16.750Z", + "name": "e2e-renamed-1788067636", + "updated": "2026-08-30T05:27:17.022Z" + }, + { + "id": "ZemxmgsQpKSVfFD2HCdx", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:08:28.663Z", + "name": "e2e-renamed-1788073708", + "updated": "2026-08-30T07:08:28.944Z" + }, + { + "id": "RAQGXM6sAILv0beGuMXX", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:49:01.776Z", + "name": "e2e-renamed-1788076141", + "updated": "2026-08-30T07:49:02.047Z" + }, + { + "id": "haJIvslEeBKfiRkR04fO", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:13:04.504Z", + "name": "e2e-renamed-1788099184", + "updated": "2026-08-30T14:13:04.758Z" + }, + { + "id": "QYH1Jw5yUIs6gEhw5ZVC", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:44:15.016Z", + "name": "e2e-renamed-1788101055", + "updated": "2026-08-30T14:44:15.286Z" + }, + { + "id": "NYrXd3ZuNFbN7up4r5sn", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-09-07T22:20:49.675Z", + "name": "e2e-renamed-1788819649", + "updated": "2026-09-07T22:20:49.944Z" + }, + { + "id": "JYYPKhwR1UiN9ar59HqT", + "created": "2026-06-02T05:34:04.233Z", + "linkCount": 0, + "name": "link-test-folder-1780378401", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "updated": "2026-06-02T05:34:04.233Z", + "color": "#0077ff" + }, + { + "id": "EGpiy7jqyjLWWn0AGma3", + "updated": "2026-06-02T16:55:35.148Z", + "name": "link-test-folder-1780419288", + "created": "2026-06-02T16:55:35.148Z", + "linkCount": 0, + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "CmAqeePRXRaEqJg93ItV", + "updated": "2026-06-02T17:39:15.784Z", + "color": "#0077ff", + "name": "link-test-folder-1780421908", + "linkCount": 0, + "created": "2026-06-02T17:39:15.784Z", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "d8ZqX8tDCLrBOmLZZPwA", + "created": "2026-06-02T18:24:11.215Z", + "linkCount": 0, + "updated": "2026-06-02T18:24:11.215Z", + "color": "#0077ff", + "name": "link-test-folder-1780424566", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "uHPt0t7aoK1iX3rVHYHT", + "created": "2026-06-02T18:30:17.026Z", + "updated": "2026-06-02T18:30:17.026Z", + "name": "link-test-folder-1780424932", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "linkCount": 0 + }, + { + "id": "iFlRAuNzdhg90QzGufRn", + "updated": "2026-06-02T18:34:42.667Z", + "created": "2026-06-02T18:34:42.667Z", + "linkCount": 0, + "name": "link-test-folder-1780425195", + "color": "#0077ff", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33" + }, + { + "id": "7grQUOVa6mnJVfaiCPqL", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1780430582", + "color": "#0077ff", + "updated": "2026-06-02T20:04:28.933Z", + "created": "2026-06-02T20:04:28.933Z", + "linkCount": 0 + }, + { + "id": "BYJZ3MnpT2oKqOS14fOB", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1780704968", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-06T00:17:00.004Z", + "updated": "2026-06-06T00:17:00.004Z" + }, + { + "id": "E1gGvUmFCDEnKroQd4ac", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1781128860", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-10T22:02:24.892Z", + "updated": "2026-06-10T22:02:24.892Z" + }, + { + "id": "G1HBfmpxK2UjfTvyjLx4", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1781147638", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-11T03:14:48.401Z", + "updated": "2026-06-11T03:14:48.401Z" + }, + { + "id": "fDexyUDX8v8VMZ7wEzpd", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1782362678", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-25T04:45:29.974Z", + "updated": "2026-06-25T04:45:29.974Z" + }, + { + "id": "EazI7EnBZ7FGGOmNWtlf", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1782863028", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-06-30T23:44:39.613Z", + "updated": "2026-06-30T23:44:39.613Z" + }, + { + "id": "7E9yQ4J4dbddQmIvbMpB", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1783131265", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-04T02:15:14.351Z", + "updated": "2026-07-04T02:15:14.351Z" + }, + { + "id": "10vyk51lPV3ZFoAelzQM", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1783542872", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-08T20:35:22.114Z", + "updated": "2026-07-08T20:35:22.114Z" + }, + { + "id": "mPKSRMoeXDtEv2jKmbqI", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1783963455", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T17:25:18.583Z", + "updated": "2026-07-13T17:25:18.583Z" + }, + { + "id": "oq3ZktWleRtfJOwDeCbd", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1783966541", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T18:16:43.567Z", + "updated": "2026-07-13T18:16:43.567Z" + }, + { + "id": "tAAb76VcYGLOExUm3EX9", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1783984192", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-07-13T23:10:52.639Z", + "updated": "2026-07-13T23:10:52.639Z" + }, + { + "id": "mtxIbV0jIWuQ1n8ac3gA", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1785952297", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-05T17:53:04.954Z", + "updated": "2026-08-05T17:53:04.954Z" + }, + { + "id": "aas0DbkHLIkHQsJgh2nE", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1787324431", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-21T15:01:19.470Z", + "updated": "2026-08-21T15:01:19.470Z" + }, + { + "id": "ES9TRkKTBYg704nlylR8", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788028904", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-29T18:42:35.897Z", + "updated": "2026-08-29T18:42:35.897Z" + }, + { + "id": "3FFhLLbLPDQYcViPY5Sy", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788065835", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T04:58:11.660Z", + "updated": "2026-08-30T04:58:11.660Z" + }, + { + "id": "uHSv6g7KuId7IFTldxeY", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788067424", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T05:24:42.446Z", + "updated": "2026-08-30T05:24:42.446Z" + }, + { + "id": "s1oUwNAcGlV78BXfU3Jm", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788073509", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:05:57.653Z", + "updated": "2026-08-30T07:05:57.653Z" + }, + { + "id": "RBXjtv01tiecps6Ou2hj", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788075945", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T07:46:32.847Z", + "updated": "2026-08-30T07:46:32.847Z" + }, { - "shortCode": "abc123", - "timestamp": "2026-09-01T00:00:00.000Z", - "country": "MX" - } - ], - "count": 1 - } - }, - "note": "path is /api/user/clicks/recent (NOT /api/user/recent-clicks); requires features.liveGlobe on the account (403 FEATURE_DISABLED otherwise); verified live envelope {clicks,count}" - }, - { - "id": "profile_get", - "capability": "12", - "request": { - "method": "GET", - "path": "/api/user/profile", - "query": {}, - "body": null - }, - "response": { - "status": 200, - "body": { - "uid": "u1", - "email": "t@example.com", - "displayName": "T", - "subscriptionTier": "pro" - } - } - }, - { - "id": "profile_update", - "capability": "13", - "request": { - "method": "PATCH", - "path": "/api/user/profile", - "query": {}, - "body": { - "displayName": "New" - } - }, - "response": { - "status": 200, - "body": { - "success": true, - "displayName": "New" - } - }, - "note": "body keys are camelCase (user.js:212 reads displayName)" - }, - { - "id": "qr_url", - "capability": "14", - "request": { - "method": "GET", - "path": "/api/qr/abc123", - "query": { - "size": "300", - "color": "000000", - "bgColor": "ffffff" - }, - "body": null - }, - "response": { - "status": 200, - "body": {} - }, - "note": "client-side URL builder; assert exact URL string" - }, - { - "id": "qr_settings_get", - "capability": "15", - "request": { - "method": "GET", - "path": "/api/link/abc123/qr-settings", - "query": {}, - "body": null - }, - "response": { - "status": 200, - "body": { - "size": 300, - "color": "#000000", - "bgColor": "#ffffff", - "logo": null - } - } - }, - { - "id": "qr_settings_update", - "capability": "16", - "request": { - "method": "PUT", - "path": "/api/link/abc123/qr-settings", - "query": {}, - "body": { - "color": "#ff0000" - } - }, - "response": { - "status": 200, - "body": { - "success": true, - "color": "#ff0000" - } - } - }, - { - "id": "folders_list", - "capability": "17", - "request": { - "method": "GET", - "path": "/api/v1/folders", - "query": {}, - "body": null - }, - "response": { - "status": 200, - "body": { - "folders": [ + "id": "j0WQFDGncerNqyc8aMlf", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788098984", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:10:31.838Z", + "updated": "2026-08-30T14:10:31.838Z" + }, { - "id": "f1", - "name": "Work", - "color": "#00f", - "linkCount": 2 + "id": "6YibiVIgVzULHQ1strVK", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788100444", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-08-30T14:37:26.607Z", + "updated": "2026-08-30T14:37:26.607Z" + }, + { + "id": "3ce0iuxdIExLNPobnX8q", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "link-test-folder-1788819465", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-09-07T22:18:32.639Z", + "updated": "2026-09-07T22:18:32.639Z" + }, + { + "id": "Y6hF8JCzWdz9rIJ0SYhk", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "SDK Test Folder 1788788924916", + "color": "#0077ff", + "created": "2026-09-07T13:48:45.267Z", + "updated": "2026-09-07T13:48:45.268Z", + "linkCount": 1 + }, + { + "id": "God5tH04sJGBqWu4MFKk", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "name": "sdk-test-folder-1788787346941", + "color": "#0077ff", + "linkCount": 0, + "created": "2026-09-07T13:22:27.223Z", + "updated": "2026-09-07T13:22:27.223Z" } ], - "limit": 10 + "limit": -1 } }, "note": "limit is tier quota, not pagination" @@ -763,10 +2660,85 @@ "body": { "views": [ { - "id": "v1", - "name": "Mine", + "id": "Vt0iywVPxyodCTUFebpp", + "name": "Folder View Cross Test", + "filters": { + "folderId": "In8iGGFI6hbBBR2EIaBL" + }, + "createdAt": { + "_seconds": 1788820649, + "_nanoseconds": 681000000 + }, + "updatedAt": { + "_seconds": 1788820649, + "_nanoseconds": 681000000 + } + }, + { + "id": "ILU2w0vcajXlVT83Al7e", + "name": "Date Range View", + "filters": {}, + "createdAt": { + "_seconds": 1788820100, + "_nanoseconds": 968000000 + }, + "updatedAt": { + "_seconds": 1788820100, + "_nanoseconds": 968000000 + } + }, + { + "id": "kAV2qF5WXgJ3UJ2h3n1I", + "name": "Tag Filtered View", + "filters": {}, + "createdAt": { + "_seconds": 1788820100, + "_nanoseconds": 728000000 + }, + "updatedAt": { + "_seconds": 1788820100, + "_nanoseconds": 728000000 + } + }, + { + "id": "MZfhfvX0yEJMDgTtwIzO", + "name": "Folder Filtered View", "filters": { - "tag": "a" + "folderId": "In8iGGFI6hbBBR2EIaBL" + }, + "createdAt": { + "_seconds": 1788820100, + "_nanoseconds": 452000000 + }, + "updatedAt": { + "_seconds": 1788820100, + "_nanoseconds": 452000000 + } + }, + { + "id": "kKJsIbklzvWgOIlRU3E0", + "name": "Pro Limit Check", + "filters": {}, + "createdAt": { + "_seconds": 1788820099, + "_nanoseconds": 341000000 + }, + "updatedAt": { + "_seconds": 1788820099, + "_nanoseconds": 341000000 + } + }, + { + "id": "WahLkndgpfXOy8e0Qa6j", + "name": "Pro Test View", + "filters": {}, + "createdAt": { + "_seconds": 1788820097, + "_nanoseconds": 838000000 + }, + "updatedAt": { + "_seconds": 1788820097, + "_nanoseconds": 838000000 } } ] @@ -835,26 +2807,31 @@ } }, { - "id": "utm_list_via_me", + "id": "utm_list", "capability": "29", "request": { "method": "GET", - "path": "/api/v1/me", + "path": "/api/user/utm-templates", "query": {}, "body": null }, "response": { "status": 200, "body": { - "utmTemplates": [ + "templates": [ { "id": "t1", - "name": "Launch" + "name": "Launch", + "source": "newsletter", + "medium": "email", + "campaign": "sept", + "term": "", + "content": "" } ] } }, - "note": "list derived from me.utmTemplates" + "note": "GET /api/user/utm-templates (apiKeyAuth) added in #833 \u2014 live on staging, pending prod deploy; returns {templates:[\u2026]}" }, { "id": "utm_create", @@ -865,18 +2842,27 @@ "query": {}, "body": { "name": "Launch", - "utmSource": "newsletter", - "utmMedium": "email", - "utmCampaign": "sept" + "source": "newsletter", + "medium": "email", + "campaign": "sept" } }, "response": { "status": 200, "body": { - "id": "t1", - "name": "Launch" + "success": true, + "template": { + "id": "t1", + "name": "Launch", + "source": "newsletter", + "medium": "email", + "campaign": "sept", + "term": "", + "content": "" + } } - } + }, + "note": "platform reads source/medium/campaign/term/content (user.js:355); currently 500s (uuidv4 undefined, issue #831)" }, { "id": "utm_delete", @@ -907,9 +2893,23 @@ "status": 200, "body": { "eventTypes": [ + "link.click", "link.created", - "link.clicked" - ] + "link.updated", + "link.deleted", + "link.expired", + "link.limit_reached", + "link.geo_blocked" + ], + "descriptions": { + "link.click": "Triggered when someone clicks on your short link", + "link.created": "Triggered when you create a new short link", + "link.updated": "Triggered when you update a link's settings", + "link.deleted": "Triggered when you delete a link", + "link.expired": "Triggered when a link expires", + "link.limit_reached": "Triggered when a link reaches its max click limit", + "link.geo_blocked": "Triggered when a click is blocked due to georestrictions" + } } } }, @@ -925,35 +2925,8 @@ "response": { "status": 200, "body": { - "webhooks": [ - { - "id": "w1", - "url": "https://h.example/", - "events": [ - "link.created" - ], - "name": "Unnamed Webhook", - "secret": "whsec_x", - "enabled": true, - "failureCount": 0, - "successCount": 0, - "lastTriggered": null, - "createdAt": "2026-09-01T00:00:00.000Z", - "updatedAt": null - }, - { - "id": "w0", - "url": "https://legacy.example/", - "events": [ - "link.click" - ], - "name": "Legacy", - "createdAt": "2026-06-01T00:00:00.000Z", - "lastDeliveryAt": null, - "lastStatus": null - } - ], - "limit": 5 + "webhooks": [], + "limit": 10 } }, "note": "serializeWebhook spreads the doc (services/webhooks.js:82); legacy docs (seen live on staging) lack enabled/secret \u2014 SDK models must treat every field except id/url/events as optional" @@ -1074,10 +3047,22 @@ "domains": [ { "domain": "go.example.com", - "status": "pending", - "verified": false + "userId": "u1", + "type": "custom", + "verificationToken": "abc", + "verifiedAt": null, + "sslStatus": "pending", + "sslCertExpiresAt": null, + "stripeSubscriptionItemId": null, + "billingStartDate": null, + "createdAt": "2026-09-01T00:00:00.000Z", + "linkCount": 0, + "lastError": null, + "status": "pending_txt", + "updatedAt": null } - ] + ], + "monthlyPrice": 500 } } }, @@ -1096,16 +3081,21 @@ "status": 200, "body": { "domain": "go.example.com", - "status": "pending", - "dnsRecords": [ - { - "type": "TXT", - "name": "_awsys", - "value": "x" - } - ] + "status": "pending_txt", + "verificationToken": "abcdef0123456789", + "txtRecord": { + "name": "_awsys-verify.go.example.com", + "type": "TXT", + "value": "awsys-verify=abcdef0123456789" + }, + "cnameRecord": { + "name": "go.example.com", + "type": "CNAME", + "value": "custom.awsys.co" + } } - } + }, + "note": "201 returns verificationToken + txtRecord{name,type,value} + cnameRecord{...} (domains.js:51-55); NOT dnsRecords[]" }, { "id": "domain_verify", @@ -1210,9 +3200,20 @@ "status": 200, "body": { "hasAccess": true, - "namespace": "acme", - "tier": "pro", - "upgradeRequired": false + "namespace": "e2ens1780419603", + "namespaceData": { + "userEmail": "test-pro-1780377090207@awsys-test.com", + "isActive": true, + "tier": "builder", + "userId": "Z40Bc0V1Q2SvKtdkElqT30y1hL33", + "claimedAt": { + "_seconds": 1780419604, + "_nanoseconds": 47000000 + } + }, + "tier": "builder", + "canClaimSubdomain": false, + "canClaimCustomDomain": true } } }, @@ -1285,8 +3286,25 @@ "status": 200, "body": { "id": "p1", - "name": "P", - "commissionRate": 10 + "merchantId": "u1", + "name": "Launch Affiliate", + "description": "desc", + "commissionType": "cpc", + "cpcRate": 0.5, + "cpaRate": 0, + "cookieDurationDays": 30, + "maxPartners": 100, + "partnerCount": 1, + "status": "active", + "isPublic": true, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + } } } }, @@ -1305,7 +3323,25 @@ "programs": [ { "id": "p1", - "name": "P" + "merchantId": "u1", + "name": "Launch Affiliate", + "description": "desc", + "commissionType": "cpc", + "cpcRate": 0.5, + "cpaRate": 0, + "cookieDurationDays": 30, + "maxPartners": 100, + "partnerCount": 1, + "status": "active", + "isPublic": true, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + } } ] } @@ -1324,7 +3360,25 @@ "status": 200, "body": { "id": "p1", - "name": "P" + "merchantId": "u1", + "name": "Launch Affiliate", + "description": "desc", + "commissionType": "cpc", + "cpcRate": 0.5, + "cpaRate": 0, + "cookieDurationDays": 30, + "maxPartners": 100, + "partnerCount": 1, + "status": "active", + "isPublic": true, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + } } } }, @@ -1343,7 +3397,25 @@ "status": 200, "body": { "id": "p1", - "name": "P2" + "merchantId": "u1", + "name": "P2", + "description": "desc", + "commissionType": "cpc", + "cpcRate": 0.5, + "cpaRate": 0, + "cookieDurationDays": 30, + "maxPartners": 100, + "partnerCount": 1, + "status": "active", + "isPublic": true, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + } } } }, @@ -1423,11 +3495,18 @@ "programs": [ { "id": "p9", - "name": "Other" + "name": "Other Program", + "description": "desc", + "commissionType": "cpa", + "cpcRate": 0, + "cpaRate": 500, + "cookieDurationDays": 30, + "partnerCount": 5 } ] } - } + }, + "note": "discover = PUBLIC SUMMARY subset: id,name,description,commissionType,cpcRate,cpaRate,cookieDurationDays,partnerCount. NO status/merchantId/maxPartners/isPublic/timestamps. Distinct type from the owned AffiliateProgram." }, { "id": "affiliate_join", @@ -1445,7 +3524,33 @@ "body": { "id": "ps1", "programId": "p9", - "status": "pending" + "partnerId": "u2", + "partnerEmail": "p@example.com", + "partnerCode": "CODE", + "status": "pending", + "stats": { + "totalClicks": 3, + "uniqueClicks": 2, + "conversions": 0, + "pendingEarnings": 0, + "paidEarnings": 0 + }, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "program": { + "id": "p9", + "name": "Other", + "commissionType": "cpa", + "cpcRate": 0, + "cpaRate": 500, + "status": "active" + } } } }, @@ -1464,7 +3569,34 @@ "partnerships": [ { "id": "ps1", - "programId": "p9" + "programId": "p9", + "partnerId": "u2", + "partnerEmail": "p@example.com", + "partnerCode": "CODE", + "status": "approved", + "stats": { + "totalClicks": 3, + "uniqueClicks": 2, + "conversions": 0, + "pendingEarnings": 0, + "paidEarnings": 0 + }, + "createdAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "updatedAt": { + "_seconds": 1788900000, + "_nanoseconds": 0 + }, + "program": { + "id": "p9", + "name": "Other", + "commissionType": "cpa", + "cpcRate": 0, + "cpaRate": 500, + "status": "active" + } } ] } @@ -1516,16 +3648,21 @@ "response": { "status": 200, "body": { - "programs": { - "used": 1, - "limit": 3 + "tier": "builder", + "limits": { + "canCreatePrograms": true, + "maxPrograms": 3, + "maxPartnersPerProgram": 100, + "canJoinAsPartner": true, + "maxPartnerships": 10 }, - "partnerships": { - "used": 1, - "limit": 10 + "usage": { + "programs": 1, + "partnerships": 1 } } - } + }, + "note": "shape is {tier,limits,usage} (affiliate.js:184); usage={programs,partnerships}" }, { "id": "agentlink_link_stats", @@ -1775,10 +3912,13 @@ "response": { "status": 200, "body": { - "shortCode": "abc123", - "trustScore": 95, - "trustStatus": "safe", - "threats": [] + "short": "3iWwb7", + "trustScore": 88, + "trustStatus": "trusted", + "threats": [], + "scannedAt": "2026-09-09T10:38:23.189Z", + "source": "gsb+heuristics", + "createdAt": 1788950303155 } }, "auth": "optional", diff --git a/tests/test_affiliate.py b/tests/test_affiliate.py index 3a4195a..159647d 100644 --- a/tests/test_affiliate.py +++ b/tests/test_affiliate.py @@ -16,7 +16,7 @@ "commissionType": "cpc", "cpcRate": 0.5, "cpaRate": None, - "cookieDays": 30, + "cookieDurationDays": 30, "status": "active", } @@ -80,7 +80,7 @@ def test_update_program_maps_fields(self): resource.update_program("prog1", cpc_rate=1.0, cookie_days=60) body = resource._http.patch.call_args[1]["json"] assert body["cpcRate"] == 1.0 - assert body["cookieDays"] == 60 + assert body["cookieDurationDays"] == 60 def test_get_program_stats_calls_endpoint(self): resource = _make_resource() diff --git a/tests/test_contract.py b/tests/test_contract.py index 1e981c5..87c33c9 100644 --- a/tests/test_contract.py +++ b/tests/test_contract.py @@ -124,7 +124,10 @@ def _h_create_link(r, e): _set_json(r.http, "post", e["response"]["body"]) result = r.links.create(e["request"]["body"]["url"]) r.http.post.assert_called_once_with("/api/v1/links", json=e["request"]["body"]) - assert result.short_code == e["response"]["body"]["shortCode"] + body = e["response"]["body"] + assert result.short_code == body["shortCode"] + assert result.trust_status == body["trustStatus"] + assert result.threats == body["threats"] def _h_create_link_custom_slug(r, e): @@ -198,7 +201,12 @@ def _h_aggregate_stats(r, e): short = e["request"]["path"].split("/")[4] result = r.analytics.get_aggregate_stats(short, period=e["request"]["query"]["period"]) r.http.get.assert_called_once_with(e["request"]["path"], params=e["request"]["query"]) - assert result.total_clicks == e["response"]["body"]["totalClicks"] + body = e["response"]["body"] + assert result.total_clicks == body["totalClicks"] + assert result.bot_clicks_excluded == body["botClicksExcluded"] + assert result.clicks_by_day[0].date == body["clicksByDay"][0]["date"] + assert result.country_breakdown == body["countryBreakdown"] + assert result.device_breakdown.desktop == body["deviceBreakdown"]["desktop"] def _h_bulk_create(r, e): @@ -337,19 +345,22 @@ def _h_view_delete(r, e): r.http.delete.assert_called_once_with(e["request"]["path"]) -def _h_utm_list_via_me(r, e): +def _h_utm_list(r, e): _set_json(r.http, "get", e["response"]["body"]) result = r.utm_templates.list() - r.http.get.assert_called_once_with("/api/v1/me") - assert len(result) == len(e["response"]["body"]["utmTemplates"]) + r.http.get.assert_called_once_with("/api/user/utm-templates") + assert len(result) == len(e["response"]["body"]["templates"]) def _h_utm_create(r, e): _set_json(r.http, "post", e["response"]["body"]) body = e["request"]["body"] - r.utm_templates.create(body["name"], body["utmSource"], body["utmMedium"], body["utmCampaign"]) + r.utm_templates.create(body["name"], body["source"], body["medium"], body["campaign"]) called_body = r.http.post.call_args[1]["json"] assert called_body["name"] == body["name"] + assert called_body["source"] == body["source"] + assert called_body["medium"] == body["medium"] + assert called_body["campaign"] == body["campaign"] def _h_utm_delete(r, e): @@ -442,8 +453,12 @@ def _h_domain_check(r, e): def _h_namespace_get(r, e): _set_json(r.http, "get", e["response"]["body"]) - r.namespace.get() + result = r.namespace.get() r.http.get.assert_called_once_with("/api/user/namespace") + body = e["response"]["body"] + assert result.can_claim_custom_domain == body["canClaimCustomDomain"] + assert result.can_claim_subdomain == body["canClaimSubdomain"] + assert result.namespace_data == body["namespaceData"] def _h_namespace_check(r, e): @@ -478,8 +493,15 @@ def _h_affiliate_programs_list(r, e): def _h_affiliate_program_get(r, e): _set_json(r.http, "get", e["response"]["body"]) - r.affiliate.get_program("p1") + result = r.affiliate.get_program("p1") r.http.get.assert_called_once_with(e["request"]["path"]) + body = e["response"]["body"] + assert result.merchant_id == body["merchantId"] + assert result.cookie_days == body["cookieDurationDays"] + assert result.max_partners == body["maxPartners"] + assert result.partner_count == body["partnerCount"] + assert result.is_public == body["isPublic"] + assert isinstance(result.created_at, str) # coerced from Firestore {_seconds,...} def _h_affiliate_program_update(r, e): @@ -631,7 +653,11 @@ def _h_trust_scan(r, e): _set_json(r.http, "get", e["response"]["body"]) result = r.trust_score.scan("abc123") r.http.get.assert_called_once_with(e["request"]["path"]) - assert result.score == e["response"]["body"]["trustScore"] + body = e["response"]["body"] + assert result.score == body["trustScore"] + assert result.short == body["short"] + assert result.source == body["source"] + assert isinstance(result.created_at, str) # coerced from the raw epoch-ms int CAPABILITY_HANDLERS = { @@ -668,7 +694,7 @@ def _h_trust_scan(r, e): "view_create": _h_view_create, "view_update": _h_view_update, "view_delete": _h_view_delete, - "utm_list_via_me": _h_utm_list_via_me, + "utm_list": _h_utm_list, "utm_create": _h_utm_create, "utm_delete": _h_utm_delete, "webhook_event_types": _h_webhook_event_types, diff --git a/tests/test_models.py b/tests/test_models.py index 4d4e743..e42fa1c 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -2,7 +2,7 @@ from __future__ import annotations -from awsysco.models import Folder, Link, LinkList +from awsysco.models import AggregateAnalytics, Folder, Link, LinkList, NamespaceInfo, TrustScoreResult class TestTimestampCoercion: @@ -105,3 +105,109 @@ def test_top_level_has_more_key_is_not_used(self): def test_missing_pagination_object_leaves_has_more_none(self): result = LinkList.model_validate({"links": []}) assert result.has_more is None + + +class TestPlatformVerifiedFieldMappings: + """Regression tests for ADR-022: an earlier round of models was built from + fixtures that turned out to be wrong about several response shapes. Each + test here validates through the TYPED field (not `.model_extra`), against + the exact shape platform-verified against live staging, so a future wrong + alias fails loudly instead of silently yielding None.""" + + def test_trust_score_result_real_shape(self): + result = TrustScoreResult.model_validate( + { + "short": "3iWwb7", + "trustScore": 88, + "trustStatus": "trusted", + "threats": [], + "scannedAt": "2026-09-09T10:38:23.189Z", + "source": "gsb+heuristics", + "createdAt": 1788950303155, # epoch milliseconds, not Firestore-shaped + } + ) + assert result.short == "3iWwb7" + assert result.score == 88 + assert result.status == "trusted" + assert result.source == "gsb+heuristics" + assert result.scanned_at == "2026-09-09T10:38:23.189Z" + assert result.created_at == "2026-09-09T10:38:23.155000Z" + + def test_namespace_info_real_shape(self): + result = NamespaceInfo.model_validate( + { + "hasAccess": True, + "namespace": "e2ens1780419603", + "namespaceData": { + "userEmail": "test@example.com", + "isActive": True, + "tier": "builder", + "userId": "abc123", + "claimedAt": {"_seconds": 1780419604, "_nanoseconds": 47000000}, + }, + "tier": "builder", + "canClaimSubdomain": False, + "canClaimCustomDomain": True, + } + ) + assert result.can_claim_custom_domain is True + assert result.can_claim_subdomain is False + assert result.namespace_data["userEmail"] == "test@example.com" + assert result.upgrade_required is None # never sent by the platform + + def test_aggregate_analytics_real_shape(self): + result = AggregateAnalytics.model_validate( + { + "shortCode": "abc123", + "period": "7d", + "totalClicks": 1, + "botClicksExcluded": 0, + "uniqueVisitors": 1, + "clicksByDay": [{"date": "2026-09-01", "clicks": 1}], + "countryBreakdown": {"MX": 1}, + "deviceBreakdown": {"mobile": 0, "desktop": 1, "tablet": 0}, + "browserBreakdown": {"Chrome": 1}, + "osBreakdown": {"macOS": 1}, + "hourBreakdown": [{"hour": 0, "clicks": 1}], + "tierLimit": 90, + "tier": "builder", + } + ) + assert result.bot_clicks_excluded == 0 + assert result.clicks_by_day[0].date == "2026-09-01" + assert result.country_breakdown == {"MX": 1} + assert result.device_breakdown.desktop == 1 + assert result.hour_breakdown[0].hour == 0 + + def test_link_real_shape_from_create_response(self): + link = Link.model_validate( + { + "success": True, + "shortUrl": "https://awsys.co/abc123", + "shortCode": "abc123", + "long": "https://example.com/", + "expireFallbackUrl": None, + "trustScore": None, + "trustStatus": "pending", + "threats": [], + } + ) + assert link.trust_status == "pending" + assert link.threats == [] + + def test_link_extended_fields_typed_not_extras(self): + link = Link.model_validate( + { + "id": "x", + "isCustom": True, + "isDisabled": True, + "disabledReason": "abuse", + "geoRestriction": {"allowedCountries": ["US"]}, + "ogMeta": {"title": "Hi"}, + } + ) + assert link.is_custom is True + assert link.is_disabled is True + assert link.disabled_reason == "abuse" + assert link.geo_restriction.allowed_countries == ["US"] + assert link.og_meta.title == "Hi" diff --git a/tests/test_utm_templates.py b/tests/test_utm_templates.py index 1bad103..c4f9598 100644 --- a/tests/test_utm_templates.py +++ b/tests/test_utm_templates.py @@ -2,34 +2,34 @@ from __future__ import annotations -from unittest.mock import MagicMock - +import asyncio +from unittest.mock import AsyncMock, MagicMock +from awsysco.async_resources.utm_templates import AsyncUtmTemplatesResource from awsysco.models import UtmTemplate from awsysco.resources.utm_templates import UtmTemplatesResource -def _make_resource(me_response=None): +def _make_resource(list_response=None): http = MagicMock() - if me_response is not None: - http.get.return_value = me_response + if list_response is not None: + http.get.return_value = list_response else: http.get.return_value = { - "uid": "user1", - "utmTemplates": [ + "templates": [ {"id": "t1", "name": "Google Ads", "source": "google", "medium": "cpc", "campaign": "brand"}, ], } - http.post.return_value = {"id": "t2", "name": "New"} + http.post.return_value = {"success": True, "template": {"id": "t2", "name": "New"}} http.delete.return_value = None return UtmTemplatesResource(http) class TestUtmTemplates: - def test_list_calls_me_endpoint(self): + def test_list_calls_correct_endpoint(self): resource = _make_resource() resource.list() - resource._http.get.assert_called_once_with("/api/v1/me") + resource._http.get.assert_called_once_with("/api/user/utm-templates") def test_list_returns_utm_templates(self): resource = _make_resource() @@ -38,7 +38,7 @@ def test_list_returns_utm_templates(self): assert all(isinstance(t, UtmTemplate) for t in result) def test_list_returns_empty_when_no_templates(self): - resource = _make_resource(me_response={"uid": "user1"}) + resource = _make_resource(list_response={"templates": []}) result = resource.list() assert result == [] @@ -70,3 +70,47 @@ def test_delete_calls_correct_endpoint(self): resource = _make_resource() resource.delete("t1") resource._http.delete.assert_called_once_with("/api/user/utm-templates/t1") + + +def _make_async_resource(list_response=None): + http = MagicMock() + http.get = AsyncMock( + return_value=list_response + or { + "templates": [ + {"id": "t1", "name": "Google Ads", "source": "google", "medium": "cpc", "campaign": "brand"}, + ], + } + ) + http.post = AsyncMock(return_value={"success": True, "template": {"id": "t2", "name": "New"}}) + http.delete = AsyncMock(return_value=None) + return AsyncUtmTemplatesResource(http) + + +class TestUtmTemplatesAsync: + def test_list_calls_correct_endpoint(self): + resource = _make_async_resource() + asyncio.run(resource.list()) + resource._http.get.assert_awaited_once_with("/api/user/utm-templates") + + def test_list_returns_utm_templates(self): + resource = _make_async_resource() + result = asyncio.run(resource.list()) + assert isinstance(result, list) + assert all(isinstance(t, UtmTemplate) for t in result) + + def test_list_returns_empty_when_no_templates(self): + resource = _make_async_resource(list_response={"templates": []}) + result = asyncio.run(resource.list()) + assert result == [] + + def test_create_calls_correct_endpoint(self): + resource = _make_async_resource() + asyncio.run(resource.create("My Template", "google", "cpc", "brand")) + resource._http.post.assert_awaited_once() + assert resource._http.post.call_args[0][0] == "/api/user/utm-templates" + + def test_delete_calls_correct_endpoint(self): + resource = _make_async_resource() + asyncio.run(resource.delete("t1")) + resource._http.delete.assert_awaited_once_with("/api/user/utm-templates/t1")