@vizzly-testing/cli 0.21.0 → 0.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1018 @@
1
+ # Vizzly SDK API Documentation
2
+
3
+ This document describes the public API endpoints available for Vizzly SDKs and CLI tools.
4
+
5
+ ## Base URL
6
+
7
+ All SDK endpoints are prefixed with `/api/sdk/`
8
+
9
+ ## Authentication
10
+
11
+ All SDK endpoints require:
12
+
13
+ - **API Token**: Passed via `Authorization: Bearer <token>` header
14
+ - **User-Agent**: Must contain "Vizzly" in the header value (e.g., `Vizzly-CLI/1.0.0`)
15
+
16
+ ## Build Management
17
+
18
+ ### Create Build
19
+
20
+ Creates a new build for screenshot uploads and comparisons. Supports both standalone builds and parallel execution for sharded test runs.
21
+
22
+ **Endpoint:** `POST /api/sdk/builds`
23
+
24
+ **Request Body:**
25
+
26
+ ```json
27
+ {
28
+ "build": {
29
+ "name": "CLI Build 2024-01-15T10:30:00Z",
30
+ "branch": "feature/new-ui",
31
+ "commit_sha": "abc123def456",
32
+ "commit_message": "Add new user dashboard",
33
+ "common_ancestor_sha": "def456ghi789",
34
+ "environment": "test",
35
+ "github_pull_request_number": 123,
36
+
37
+ // Parallel execution fields (optional)
38
+ "parallel_id": "workflow-12345-attempt-1"
39
+ }
40
+ }
41
+ ```
42
+
43
+ **Parallel Execution Field:**
44
+
45
+ - `parallel_id` (optional): Unique identifier for parallel test execution (e.g., GitHub workflow run ID)
46
+
47
+ When `parallel_id` is provided:
48
+
49
+ - **First shard**: Creates a new build with the parallel_id
50
+ - **Subsequent shards**: Find and reuse the existing build with the same parallel_id
51
+ - **All shards**: Upload screenshots to the same build (leveraging existing build reopening logic)
52
+
53
+ **Response (201):**
54
+
55
+ ```json
56
+ {
57
+ "id": "build-uuid",
58
+ "name": "CLI Build 2024-01-15T10:30:00Z",
59
+ "branch": "feature/new-ui",
60
+ "environment": "test",
61
+ "status": "created",
62
+ "url": "https://app.vizzly.co/org-slug/project-slug/builds/build-uuid",
63
+ "organizationSlug": "my-org",
64
+ "projectSlug": "my-project",
65
+ "created_at": "2024-01-15T10:30:00Z"
66
+ }
67
+ ```
68
+
69
+ ### Get Build Details
70
+
71
+ Retrieves build information with optional related data.
72
+
73
+ **Endpoint:** `GET /api/sdk/builds/:buildId`
74
+
75
+ **Query Parameters:**
76
+
77
+ - `include` (optional): `"screenshots"`, `"comparisons"`, or `"review-summary"` to include related data
78
+
79
+ **Response (200):**
80
+
81
+ ```json
82
+ {
83
+ "build": {
84
+ "id": "build-uuid",
85
+ "name": "CLI Build 2024-01-15T10:30:00Z",
86
+ "branch": "feature/new-ui",
87
+ "status": "completed",
88
+ "screenshot_count": 25,
89
+ "comparison_count": 20,
90
+ "created_at": "2024-01-15T10:30:00Z",
91
+ "screenshots": [
92
+ // Only included if include=screenshots
93
+ {
94
+ "id": "screenshot-uuid",
95
+ "name": "homepage-desktop-chrome",
96
+ "build_id": "build-uuid",
97
+ "browser": "chrome",
98
+ "viewport_width": 1920,
99
+ "viewport_height": 1080,
100
+ "url": "https://example.com/page",
101
+ "sha256": "abc123def456...",
102
+ "status": "completed",
103
+ "original_url": "https://storage.example.com/screenshot.png",
104
+ "file_size_bytes": 245760,
105
+ "width": 1920,
106
+ "height": 1080,
107
+ "created_at": "2024-01-15T10:30:00Z"
108
+ }
109
+ ],
110
+ "comparisons": [], // Only included if include=comparisons
111
+ "review_summary": {
112
+ // Only included if include=review-summary
113
+ "build_id": "build-uuid",
114
+ "review_status": "in_progress",
115
+ "assignments": [
116
+ {
117
+ "id": "assignment-uuid",
118
+ "user_id": "user-uuid",
119
+ "user_name": "Jane Smith",
120
+ "status": "pending",
121
+ "assigned_at": "2024-01-15T10:00:00Z"
122
+ }
123
+ ],
124
+ "statistics": {
125
+ "total_assignments": 1,
126
+ "pending": 1,
127
+ "completed": 0
128
+ }
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ### List Builds
135
+
136
+ Retrieves a paginated list of builds with optional filtering.
137
+
138
+ **Endpoint:** `GET /api/sdk/builds`
139
+
140
+ **Query Parameters:**
141
+
142
+ - `limit` (optional, default: 20): Maximum number of builds to return
143
+ - `offset` (optional, default: 0): Number of builds to skip
144
+ - `branch` (optional): Filter by branch name
145
+ - `environment` (optional): Filter by environment
146
+ - `status` (optional): Filter by build status
147
+
148
+ **Response (200):**
149
+
150
+ ```json
151
+ {
152
+ "builds": [
153
+ {
154
+ "id": "build-uuid",
155
+ "name": "CLI Build 2024-01-15T10:30:00Z",
156
+ "branch": "main",
157
+ "environment": "test",
158
+ "status": "completed",
159
+ "created_at": "2024-01-15T10:30:00Z"
160
+ }
161
+ ],
162
+ "pagination": {
163
+ "total": 150,
164
+ "limit": 20,
165
+ "offset": 0,
166
+ "hasMore": true
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Update Build Status
172
+
173
+ Updates the status of a build (typically called by CLI when test suite completes).
174
+
175
+ **Endpoint:** `PUT /api/sdk/builds/:buildId/status`
176
+
177
+ **Request Body:**
178
+
179
+ ```json
180
+ {
181
+ "status": "completed",
182
+ "executionTimeMs": 45000
183
+ }
184
+ ```
185
+
186
+ **Valid Status Values:**
187
+
188
+ - `"pending"` → Maps to "created" (build ready for screenshots)
189
+ - `"running"` → Maps to "processing" (build is processing screenshots)
190
+ - `"completed"` → Maps to "completed" (all screenshots processed successfully)
191
+ - `"failed"` → Maps to "failed" (build failed or was terminated)
192
+
193
+ **Important: Parallel Build Behavior**
194
+
195
+ For builds with a `parallel_id`, the `"completed"` status is **not allowed** via this endpoint. Instead, you'll receive:
196
+
197
+ **Response for Parallel Build Completion Attempt (200):**
198
+
199
+ ```json
200
+ {
201
+ "build_id": "build-uuid",
202
+ "status": "processing",
203
+ "message": "Build with parallel_id cannot be finalized via status endpoint. Use POST /api/sdk/parallel/:parallelId/finalize to complete all parallel shards.",
204
+ "parallel_id": "workflow-12345-attempt-1",
205
+ "awaiting_finalize_all": true
206
+ }
207
+ ```
208
+
209
+ **Response for Regular Builds (200):**
210
+
211
+ ```json
212
+ {
213
+ "message": "Build status updated successfully",
214
+ "build": {
215
+ "id": "build-uuid",
216
+ "status": "completed",
217
+ "execution_time_ms": 45000
218
+ }
219
+ }
220
+ ```
221
+
222
+ ## Screenshot Management
223
+
224
+ ### Upload Screenshot (Real-time)
225
+
226
+ Uploads a single screenshot to an existing build (used during test execution).
227
+
228
+ **Endpoint:** `POST /api/sdk/builds/:buildId/screenshots`
229
+
230
+ **Request Body:**
231
+
232
+ ```json
233
+ {
234
+ "name": "homepage-desktop-chrome",
235
+ "image_data": "base64-encoded-image-data",
236
+ "properties": {
237
+ "browser": "chrome",
238
+ "browserVersion": "91.0.4472.124",
239
+ "device": "desktop",
240
+ "viewport": {
241
+ "width": 1920,
242
+ "height": 1080
243
+ },
244
+ "url": "https://example.com/homepage",
245
+ "selector": ".main-content"
246
+ }
247
+ }
248
+ ```
249
+
250
+ **Response (201):**
251
+
252
+ ```json
253
+ {
254
+ "id": "screenshot-uuid",
255
+ "name": "homepage-desktop-chrome",
256
+ "build_id": "build-uuid",
257
+ "status": "pending",
258
+ "sha256": "abc123...",
259
+ "isDuplicate": false,
260
+ "build_screenshot_count": 5,
261
+ "created_at": "2024-01-15T10:30:00Z"
262
+ }
263
+ ```
264
+
265
+ ### Check Existing Screenshots (Signature-Based Deduplication)
266
+
267
+ Checks if screenshots with given SHA256 hashes already exist using intelligent signature-based deduplication. Supports both legacy SHA-only format and new signature-based format.
268
+
269
+ **Endpoint:** `POST /api/sdk/check-shas`
270
+
271
+ #### Legacy Format (SHA-only)
272
+
273
+ **Request Body:**
274
+
275
+ ```json
276
+ {
277
+ "buildId": "build-uuid",
278
+ "shas": ["abc123def456...", "def456ghi789..."]
279
+ }
280
+ ```
281
+
282
+ **Response (200):**
283
+
284
+ ```json
285
+ {
286
+ "existing": ["abc123def456..."],
287
+ "missing": ["def456ghi789..."],
288
+ "summary": {
289
+ "total_checked": 2,
290
+ "existing_count": 1,
291
+ "missing_count": 1,
292
+ "screenshots_created": 1,
293
+ "format_used": "legacy-sha-only"
294
+ },
295
+ "screenshots": [
296
+ {
297
+ "id": "screenshot-uuid",
298
+ "name": "auto-generated-name",
299
+ "sha256": "abc123def456...",
300
+ "fromExisting": true,
301
+ "alreadyExisted": false
302
+ }
303
+ ]
304
+ }
305
+ ```
306
+
307
+ #### New Format (Signature-Based - Recommended)
308
+
309
+ **Request Body:**
310
+
311
+ ```json
312
+ {
313
+ "buildId": "build-uuid",
314
+ "screenshots": [
315
+ {
316
+ "sha256": "abc123def456...",
317
+ "name": "homepage-login-form",
318
+ "browser": "chrome",
319
+ "viewport_width": 1920,
320
+ "viewport_height": 1080,
321
+ "url": "https://example.com/login",
322
+ "selector": ".login-form"
323
+ },
324
+ {
325
+ "sha256": "def456ghi789...",
326
+ "name": "dashboard-mobile",
327
+ "browser": "chrome",
328
+ "viewport_width": 375,
329
+ "viewport_height": 667
330
+ }
331
+ ]
332
+ }
333
+ ```
334
+
335
+ **Response (200):**
336
+
337
+ ```json
338
+ {
339
+ "existing": [],
340
+ "missing": ["abc123def456...", "def456ghi789..."],
341
+ "summary": {
342
+ "total_checked": 2,
343
+ "existing_count": 0,
344
+ "missing_count": 2,
345
+ "screenshots_created": 0,
346
+ "format_used": "signature-based"
347
+ },
348
+ "results": [
349
+ {
350
+ "sha256": "abc123def456...",
351
+ "exists": true,
352
+ "reusable": false,
353
+ "reason": "Different signature (homepage-desktop|1920|chrome vs homepage-login-form|1920|chrome)"
354
+ },
355
+ {
356
+ "sha256": "def456ghi789...",
357
+ "exists": false,
358
+ "reusable": false,
359
+ "reason": "SHA not found"
360
+ }
361
+ ],
362
+ "screenshots": []
363
+ }
364
+ ```
365
+
366
+ #### Signature-Based Logic
367
+
368
+ The new format uses screenshot **signature** for intelligent deduplication:
369
+
370
+ - **Signature**: `name + viewport_width + browser` (excludes height as it's variable)
371
+ - **Same SHA + Same Signature**: Screenshot is reusable, returns as "existing"
372
+ - **Same SHA + Different Signature**: Screenshot not reusable, returns as "missing" to force new upload
373
+ - **Different SHA**: New screenshot, returns as "missing"
374
+
375
+ This ensures screenshots with new names but identical image data create separate records with correct names.
376
+
377
+ ### Batch Upload Screenshots
378
+
379
+ Uploads multiple screenshot files and/or references existing screenshots by SHA256.
380
+
381
+ **Endpoint:** `POST /api/sdk/upload`
382
+
383
+ **Content-Type:** `multipart/form-data`
384
+
385
+ **Form Fields:**
386
+
387
+ - `screenshots` (files): Array of screenshot image files
388
+ - `build_id` (optional): ID of existing build to upload to
389
+ - `build_name` (optional): Name for new build if creating one
390
+ - `branch` (optional): Git branch name
391
+ - `commit_sha` (optional): Git commit SHA
392
+ - `commit_message` (optional): Git commit message
393
+ - `common_ancestor_sha` (optional): Common ancestor commit SHA
394
+ - `environment` (optional, default: "test"): Environment name
395
+ - `existing_shas` (optional): JSON array of SHA256 hashes for existing screenshots
396
+ - `config` (optional): JSON configuration object
397
+ - `threshold` (optional): Comparison threshold
398
+ - `parallel_id` (optional): Unique identifier for parallel test execution
399
+
400
+ **Response (201):**
401
+
402
+ ```json
403
+ {
404
+ "message": "Screenshots uploaded successfully and queued for processing",
405
+ "build": {
406
+ "id": "build-uuid",
407
+ "name": "CLI Build",
408
+ "status": "processing"
409
+ },
410
+ "screenshots": [],
411
+ "count": 10,
412
+ "processing_time_ms": 1250,
413
+ "efficiency": {
414
+ "total_processed": 10,
415
+ "files_uploaded": 7,
416
+ "existing_shas_requested": 3,
417
+ "existing_reused": 3,
418
+ "existing_skipped": 0
419
+ }
420
+ }
421
+ ```
422
+
423
+ ## Comparison Management
424
+
425
+ ### Get Comparison Details
426
+
427
+ Retrieves detailed information about a specific comparison, including diff images and commit context for debugging.
428
+
429
+ **Endpoint:** `GET /api/sdk/comparisons/:comparisonId`
430
+
431
+ **Response (200):**
432
+
433
+ ```json
434
+ {
435
+ "comparison": {
436
+ "id": "comparison-uuid",
437
+ "build_id": "build-uuid",
438
+ "build_name": "CLI Build",
439
+ "build_branch": "feature/new-ui",
440
+ "build_commit_sha": "abc123def456",
441
+ "build_commit_message": "✨ Add new dashboard",
442
+ "build_common_ancestor_sha": "def456ghi789",
443
+ "name": "homepage-desktop-chrome",
444
+ "status": "completed",
445
+ "diff_percentage": 2.5,
446
+ "threshold": 0.1,
447
+ "has_diff": true,
448
+ "approval_status": "pending",
449
+ "current_screenshot_url": "https://storage.example.com/current.png",
450
+ "baseline_screenshot_url": "https://storage.example.com/baseline.png",
451
+ "diff_url": "https://storage.example.com/diff.png",
452
+ "created_at": "2024-01-15T10:30:00Z"
453
+ }
454
+ }
455
+ ```
456
+
457
+ ## Build Collaboration
458
+
459
+ ### Create Build Comment
460
+
461
+ Creates a comment on a build for team collaboration and feedback.
462
+
463
+ **Endpoint:** `POST /api/sdk/builds/:buildId/comments`
464
+
465
+ **Request Body:**
466
+
467
+ ```json
468
+ {
469
+ "content": "All visual diffs look good, approving this build for deployment.",
470
+ "type": "general"
471
+ }
472
+ ```
473
+
474
+ **Comment Types:**
475
+
476
+ - `"general"`: General comment or discussion
477
+ - `"approval"`: Comment indicating approval
478
+ - `"rejection"`: Comment indicating rejection with concerns
479
+
480
+ **Response (201):**
481
+
482
+ ```json
483
+ {
484
+ "comment": {
485
+ "id": "comment-uuid",
486
+ "build_id": "build-uuid",
487
+ "user_id": "user-uuid",
488
+ "author_name": "John Doe",
489
+ "content": "All visual diffs look good, approving this build for deployment.",
490
+ "type": "general",
491
+ "created_at": "2024-01-15T10:30:00Z",
492
+ "updated_at": "2024-01-15T10:30:00Z"
493
+ }
494
+ }
495
+ ```
496
+
497
+ ### List Build Comments
498
+
499
+ Retrieves all comments for a build, including nested replies.
500
+
501
+ **Endpoint:** `GET /api/sdk/builds/:buildId/comments`
502
+
503
+ **Response (200):**
504
+
505
+ ```json
506
+ {
507
+ "comments": [
508
+ {
509
+ "id": "comment-uuid",
510
+ "build_id": "build-uuid",
511
+ "user_id": "user-uuid",
512
+ "author_name": "John Doe",
513
+ "content": "All visual diffs look good, approving this build for deployment.",
514
+ "type": "general",
515
+ "created_at": "2024-01-15T10:30:00Z",
516
+ "updated_at": "2024-01-15T10:30:00Z",
517
+ "replies": []
518
+ }
519
+ ]
520
+ }
521
+ ```
522
+
523
+ **Note:** For MCP/Claude Code integration, the `profile_photo_url` and `email` fields are filtered out to reduce token usage.
524
+
525
+ ## Comparison Approvals
526
+
527
+ ### Update Comparison Approval Status
528
+
529
+ Updates the approval status of a comparison.
530
+
531
+ **Endpoint:** `PUT /api/sdk/comparisons/:comparisonId/approval`
532
+
533
+ **Request Body:**
534
+
535
+ ```json
536
+ {
537
+ "approval_status": "approved",
538
+ "comment": "Visual changes look intentional, layout improvements are good."
539
+ }
540
+ ```
541
+
542
+ **Valid Approval Status Values:**
543
+
544
+ - `"pending"`: No decision made yet (default)
545
+ - `"approved"`: Comparison approved, changes are intentional
546
+ - `"rejected"`: Comparison rejected, changes need investigation
547
+ - `"auto_approved"`: Automatically approved (identical screenshots)
548
+
549
+ **Response (200):**
550
+
551
+ ```json
552
+ {
553
+ "message": "Comparison approval status updated successfully",
554
+ "comparison": {
555
+ "id": "comparison-uuid",
556
+ "approval_status": "approved",
557
+ "approval_comment": "Visual changes look intentional, layout improvements are good.",
558
+ "approved_by": "user-uuid",
559
+ "approved_at": "2024-01-15T10:30:00Z"
560
+ }
561
+ }
562
+ ```
563
+
564
+ ### Approve Comparison
565
+
566
+ Convenient endpoint to approve a comparison with optional comment.
567
+
568
+ **Endpoint:** `POST /api/sdk/comparisons/:comparisonId/approve`
569
+
570
+ **Request Body:**
571
+
572
+ ```json
573
+ {
574
+ "comment": "Homepage redesign looks excellent, ready for production."
575
+ }
576
+ ```
577
+
578
+ **Response (200):**
579
+
580
+ ```json
581
+ {
582
+ "message": "Comparison approved successfully",
583
+ "comparison": {
584
+ "id": "comparison-uuid",
585
+ "approval_status": "approved",
586
+ "approval_comment": "Homepage redesign looks excellent, ready for production.",
587
+ "approved_by": "user-uuid",
588
+ "approved_at": "2024-01-15T10:30:00Z"
589
+ }
590
+ }
591
+ ```
592
+
593
+ ### Reject Comparison
594
+
595
+ Convenient endpoint to reject a comparison with required reason.
596
+
597
+ **Endpoint:** `POST /api/sdk/comparisons/:comparisonId/reject`
598
+
599
+ **Request Body:**
600
+
601
+ ```json
602
+ {
603
+ "reason": "Button alignment is off by 2px, needs to be fixed before deployment."
604
+ }
605
+ ```
606
+
607
+ **Response (200):**
608
+
609
+ ```json
610
+ {
611
+ "message": "Comparison rejected successfully",
612
+ "comparison": {
613
+ "id": "comparison-uuid",
614
+ "approval_status": "rejected",
615
+ "approval_comment": "Button alignment is off by 2px, needs to be fixed before deployment.",
616
+ "approved_by": "user-uuid",
617
+ "approved_at": "2024-01-15T10:30:00Z"
618
+ }
619
+ }
620
+ ```
621
+
622
+ ## Review Management
623
+
624
+ ### Get Build Review Summary
625
+
626
+ Retrieves review status, assignments, and statistics for a build.
627
+
628
+ **Endpoint:** `GET /api/sdk/builds/:buildId/review-summary`
629
+
630
+ **Response (200):**
631
+
632
+ ```json
633
+ {
634
+ "build_id": "build-uuid",
635
+ "review_status": "in_progress",
636
+ "assignments": [
637
+ {
638
+ "id": "assignment-uuid",
639
+ "user_id": "user-uuid",
640
+ "user_name": "Jane Smith",
641
+ "status": "pending",
642
+ "assigned_at": "2024-01-15T10:00:00Z",
643
+ "due_date": null
644
+ },
645
+ {
646
+ "id": "assignment-uuid-2",
647
+ "user_id": "user-uuid-2",
648
+ "user_name": "John Doe",
649
+ "status": "completed",
650
+ "assigned_at": "2024-01-15T10:00:00Z",
651
+ "completed_at": "2024-01-15T10:30:00Z",
652
+ "due_date": null
653
+ }
654
+ ],
655
+ "statistics": {
656
+ "total_assignments": 2,
657
+ "pending": 1,
658
+ "completed": 1,
659
+ "approved": 0,
660
+ "rejected": 0
661
+ }
662
+ }
663
+ ```
664
+
665
+ **Review Status Values:**
666
+
667
+ - `"pending"`: No reviewers assigned yet
668
+ - `"in_progress"`: Reviewers assigned, waiting for completion
669
+ - `"completed"`: All reviewers have completed their review
670
+ - `"approved"`: Build approved by all reviewers
671
+ - `"rejected"`: Build rejected by one or more reviewers
672
+
673
+ ## Token Management
674
+
675
+ ### Get Token Context
676
+
677
+ Retrieves information about the current API token and associated organization/project.
678
+
679
+ **Endpoint:** `GET /api/sdk/token/context`
680
+
681
+ **Response (200):**
682
+
683
+ ```json
684
+ {
685
+ "token": {
686
+ "id": "token-uuid",
687
+ "name": "CI/CD Token",
688
+ "prefix": "vzy_"
689
+ },
690
+ "organization": {
691
+ "id": "org-uuid",
692
+ "name": "My Company",
693
+ "slug": "my-company"
694
+ },
695
+ "project": {
696
+ "id": "project-uuid",
697
+ "name": "My App",
698
+ "slug": "my-app"
699
+ }
700
+ }
701
+ ```
702
+
703
+ ## Error Responses
704
+
705
+ All endpoints may return these common error responses:
706
+
707
+ ### 400 Bad Request
708
+
709
+ ```json
710
+ {
711
+ "error": "Validation error message"
712
+ }
713
+ ```
714
+
715
+ ### 400 Bad Request (Missing User-Agent)
716
+
717
+ ```json
718
+ {
719
+ "error": "User-Agent header required"
720
+ }
721
+ ```
722
+
723
+ ```json
724
+ {
725
+ "error": "Invalid User-Agent for SDK request"
726
+ }
727
+ ```
728
+
729
+ ### 401 Unauthorized
730
+
731
+ ```json
732
+ {
733
+ "error": "Invalid or missing API token"
734
+ }
735
+ ```
736
+
737
+ ### 404 Not Found
738
+
739
+ ```json
740
+ {
741
+ "error": "Resource not found"
742
+ }
743
+ ```
744
+
745
+ ### 413 Payload Too Large (Storage Quota Exceeded)
746
+
747
+ ```json
748
+ {
749
+ "error": "Storage quota exceeded",
750
+ "details": {
751
+ "currentUsage": "2.5GB",
752
+ "limit": "5.0GB",
753
+ "estimatedUpload": "3.0GB",
754
+ "planName": "Pro"
755
+ }
756
+ }
757
+ ```
758
+
759
+ ### 500 Internal Server Error
760
+
761
+ ```json
762
+ {
763
+ "error": "Internal server error message"
764
+ }
765
+ ```
766
+
767
+ ## Rate Limits
768
+
769
+ Currently, there are no enforced rate limits on SDK endpoints, but reasonable usage is expected.
770
+
771
+ ## Usage Examples
772
+
773
+ ### Complete Workflow Example
774
+
775
+ 1. **Create a build:**
776
+
777
+ ```bash
778
+ curl -X POST /api/sdk/builds \
779
+ -H "Authorization: Bearer your-api-token" \
780
+ -H "User-Agent: Vizzly-CLI/1.0.0" \
781
+ -H "Content-Type: application/json" \
782
+ -d '{"build": {"name": "Test Build", "branch": "main"}}'
783
+ ```
784
+
785
+ 2. **Upload screenshots:**
786
+
787
+ ```bash
788
+ curl -X POST /api/sdk/builds/build-uuid/screenshots \
789
+ -H "Authorization: Bearer your-api-token" \
790
+ -H "User-Agent: Vizzly-CLI/1.0.0" \
791
+ -H "Content-Type: application/json" \
792
+ -d '{"name": "homepage", "image_data": "base64..."}'
793
+ ```
794
+
795
+ 3. **Complete the build:**
796
+ ```bash
797
+ curl -X PUT /api/sdk/builds/build-uuid/status \
798
+ -H "Authorization: Bearer your-api-token" \
799
+ -H "User-Agent: Vizzly-CLI/1.0.0" \
800
+ -H "Content-Type: application/json" \
801
+ -d '{"status": "completed"}'
802
+ ```
803
+
804
+ ## Parallel Build Support
805
+
806
+ Vizzly supports parallel test execution where multiple test runners (shards) contribute screenshots to a single shared build. This is particularly useful for CI/CD workflows with parallel jobs.
807
+
808
+ ### How Parallel Builds Work
809
+
810
+ 1. **Single Build**: All shards share one build using a common `parallel_id`
811
+ 2. **First Shard Creates**: The first shard to call the API creates the build
812
+ 3. **Subsequent Shards Reuse**: Other shards find and reuse the existing build
813
+ 4. **Automatic Reopening**: Existing build reopening logic handles concurrent uploads
814
+ 5. **Shard Completion**: Individual shards can update status to `"failed"` but NOT `"completed"`
815
+ 6. **Manual Finalization**: ONLY the finalize endpoint can complete parallel builds
816
+
817
+ ### New Endpoints for Parallel Builds
818
+
819
+ #### Finalize Parallel Build
820
+
821
+ Marks a parallel build as completed after all shards finish.
822
+
823
+ **Endpoint:** `POST /api/sdk/parallel/:parallelId/finalize`
824
+
825
+ **Response (200):**
826
+
827
+ ```json
828
+ {
829
+ "message": "Parallel build finalized successfully",
830
+ "build": {
831
+ "id": "build-uuid",
832
+ "status": "completed",
833
+ "parallel_id": "workflow-12345-attempt-1"
834
+ }
835
+ }
836
+ ```
837
+
838
+ ### Usage Example (GitHub Actions)
839
+
840
+ ```yaml
841
+ jobs:
842
+ e2e-tests:
843
+ strategy:
844
+ matrix:
845
+ shard: [1/4, 2/4, 3/4, 4/4]
846
+ steps:
847
+ - name: Run tests with Vizzly
848
+ run: |
849
+ npx vizzly run "npm test -- --shard=${{ matrix.shard }}" \
850
+ --parallel-id="${{ github.run_id }}-${{ github.run_attempt }}"
851
+
852
+ finalize-e2e:
853
+ needs: e2e-tests
854
+ runs-on: ubuntu-latest
855
+ if: always() && needs.e2e-tests.result == 'success'
856
+ steps:
857
+ - name: Finalize parallel build
858
+ run: |
859
+ curl -X POST "https://api.vizzly.co/api/sdk/parallel/${{ github.run_id }}-${{ github.run_attempt }}/finalize" \
860
+ -H "Authorization: Bearer ${{ secrets.VIZZLY_TOKEN }}" \
861
+ -H "User-Agent: GitHub-Actions/1.0"
862
+ ```
863
+
864
+ ### Best Practices
865
+
866
+ - Use GitHub workflow run ID + attempt number for `parallel_id` to ensure uniqueness
867
+ - Always call the finalize endpoint after all shards complete
868
+ - The finalize job should depend on all shard jobs completing successfully
869
+
870
+ ## Hotspot Analysis (TDD Mode)
871
+
872
+ Hotspot endpoints provide historical data about regions that frequently change across builds (timestamps, dynamic IDs, ads, etc.). This data helps the CLI's TDD mode intelligently filter out known dynamic content.
873
+
874
+ ### Get Screenshot Hotspots
875
+
876
+ Retrieves hotspot analysis for a specific screenshot name.
877
+
878
+ **Endpoint:** `GET /api/sdk/screenshots/:screenshotName/hotspots`
879
+
880
+ **Query Parameters:**
881
+
882
+ - `windowSize` (optional, default: 20, max: 50): Number of historical builds to analyze
883
+
884
+ **Response (200):**
885
+
886
+ ```json
887
+ {
888
+ "screenshot_name": "homepage-desktop-chrome",
889
+ "hotspot_analysis": {
890
+ "regions": [
891
+ {
892
+ "y1": 100,
893
+ "y2": 150,
894
+ "height": 50,
895
+ "frequency": 0.85,
896
+ "lineCount": 50
897
+ }
898
+ ],
899
+ "total_builds_analyzed": 15,
900
+ "confidence": "high",
901
+ "confidence_score": 85,
902
+ "confidence_factors": {
903
+ "build_count": 15,
904
+ "data_source": "approved",
905
+ "frequency_consistency": 0.9
906
+ },
907
+ "data_source": "approved",
908
+ "frequency_threshold": 0.7
909
+ }
910
+ }
911
+ ```
912
+
913
+ **Confidence Values:**
914
+
915
+ - `"high"`: Strong historical data, reliable hotspot detection
916
+ - `"medium"`: Moderate data, reasonably reliable
917
+ - `"low"`: Limited data, use with caution
918
+ - `"insufficient_data"`: Not enough historical builds to determine hotspots
919
+
920
+ **Data Source Values:**
921
+
922
+ - `"approved"`: Hotspots derived from approved builds only (highest quality)
923
+ - `"mixed"`: Includes both approved and unapproved builds
924
+ - `"none"`: No historical data available
925
+
926
+ ### Batch Get Screenshot Hotspots
927
+
928
+ Retrieves hotspot analysis for multiple screenshots in a single request. More efficient for TDD baseline downloads.
929
+
930
+ **Endpoint:** `POST /api/sdk/screenshots/hotspots`
931
+
932
+ **Request Body:**
933
+
934
+ ```json
935
+ {
936
+ "screenshot_names": [
937
+ "homepage-desktop-chrome",
938
+ "dashboard-mobile-safari",
939
+ "settings-tablet-firefox"
940
+ ],
941
+ "windowSize": 20
942
+ }
943
+ ```
944
+
945
+ **Constraints:**
946
+
947
+ - Maximum 100 screenshot names per request
948
+ - Screenshot names must be valid (no path traversal, max 255 characters)
949
+
950
+ **Response (200):**
951
+
952
+ ```json
953
+ {
954
+ "hotspots": {
955
+ "homepage-desktop-chrome": {
956
+ "regions": [
957
+ {
958
+ "y1": 100,
959
+ "y2": 150,
960
+ "height": 50,
961
+ "frequency": 0.85,
962
+ "lineCount": 50
963
+ }
964
+ ],
965
+ "total_builds_analyzed": 15,
966
+ "confidence": "high",
967
+ "confidence_score": 85,
968
+ "data_source": "approved"
969
+ },
970
+ "dashboard-mobile-safari": {
971
+ "regions": [],
972
+ "total_builds_analyzed": 8,
973
+ "confidence": "medium",
974
+ "confidence_score": 60,
975
+ "data_source": "mixed"
976
+ }
977
+ },
978
+ "summary": {
979
+ "requested": 3,
980
+ "returned": 2
981
+ }
982
+ }
983
+ ```
984
+
985
+ **Notes:**
986
+
987
+ - Only screenshots with `total_builds_analyzed > 0` are included in the response
988
+ - Screenshots without historical data are omitted from the `hotspots` object
989
+ - The `summary` shows how many screenshots were requested vs returned
990
+
991
+ ### Usage Example (CLI TDD Mode)
992
+
993
+ ```bash
994
+ # Fetch hotspots for baseline download
995
+ curl -X POST /api/sdk/screenshots/hotspots \
996
+ -H "Authorization: Bearer your-api-token" \
997
+ -H "User-Agent: Vizzly-CLI/1.0.0" \
998
+ -H "Content-Type: application/json" \
999
+ -d '{"screenshot_names": ["homepage", "dashboard", "settings"]}'
1000
+ ```
1001
+
1002
+ The CLI uses this data to:
1003
+
1004
+ 1. Identify regions that historically change frequently
1005
+ 2. Apply intelligent filtering during local comparisons
1006
+ 3. Reduce false positives from dynamic content like timestamps or ads
1007
+
1008
+ ## Implementation Notes
1009
+
1010
+ - All image data should be base64 encoded when sent via JSON
1011
+ - File uploads use standard multipart/form-data encoding
1012
+ - SHA256 hashes are used for deduplication and efficient uploads
1013
+ - Build processing happens asynchronously after screenshot upload
1014
+ - Screenshots are automatically processed for comparisons when uploaded
1015
+ - Builds can be reopened if new screenshots arrive (useful for parallel test execution)
1016
+ - Storage quota is checked before accepting uploads
1017
+ - **Parallel builds**: Multiple shards share a single build using `parallel_id` for efficient parallel testing
1018
+ - **Build finalization**: Use the finalize endpoint to complete parallel builds after all shards finish