@livex/contracts 0.1.0 → 0.1.1

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.
@@ -1,303 +1,4536 @@
1
1
  {
2
- "openapi": "3.1.0",
3
- "info": {
4
- "title": "LIVEX API",
5
- "version": "1.0.0",
6
- "description": "Contratos v1 del MVP: catálogo, disponibilidad, bookings y webhooks de pago.",
7
- "license": { "name": "MIT", "url": "https://opensource.org/licenses/MIT" }
8
- },
9
- "servers": [{ "url": "https://api.livex.app/v1" }],
10
- "security": [
11
- { "BearerAuth": [] }
12
- ],
13
- "paths": {
14
- "/experiences": {
15
- "get": {
16
- "summary": "Listar experiencias",
17
- "operationId": "listExperiences",
18
- "security": [],
19
- "parameters": [
20
- { "name": "q", "in": "query", "schema": { "type": "string" } },
21
- { "name": "categoryId", "in": "query", "schema": { "type": "string", "format": "uuid" } },
22
- { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
23
- { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
24
- ],
25
- "responses": {
26
- "200": {
27
- "description": "OK",
28
- "content": {
29
- "application/json": {
30
- "schema": {
31
- "type": "object",
32
- "properties": {
33
- "items": { "type": "array", "items": { "$ref": "#/components/schemas/ExperienceSummary" } },
34
- "total": { "type": "integer" }
35
- },
36
- "required": ["items", "total"]
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "LIVEX API - Documentación Completa",
5
+ "version": "1.0.1",
6
+ "description": "API completa de LIVEX con todos los módulos: Auth, Users, Experiences, Categories, Resorts, Availability, Bookings, Payments, Agents, Admin"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://api.livex.app",
11
+ "description": "Producción"
12
+ },
13
+ {
14
+ "url": "http://localhost:3000",
15
+ "description": "Desarrollo"
16
+ }
17
+ ],
18
+ "security": [
19
+ {
20
+ "BearerAuth": []
21
+ }
22
+ ],
23
+ "paths": {
24
+ "/api/v1/auth/register": {
25
+ "post": {
26
+ "tags": [
27
+ "Auth"
28
+ ],
29
+ "summary": "Registrar usuario",
30
+ "security": [],
31
+ "requestBody": {
32
+ "required": true,
33
+ "content": {
34
+ "application/json": {
35
+ "schema": {
36
+ "type": "object",
37
+ "required": [
38
+ "email",
39
+ "password"
40
+ ],
41
+ "properties": {
42
+ "email": {
43
+ "type": "string",
44
+ "format": "email"
45
+ },
46
+ "password": {
47
+ "type": "string",
48
+ "minLength": 8,
49
+ "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)"
50
+ },
51
+ "fullName": {
52
+ "type": "string",
53
+ "maxLength": 120
54
+ },
55
+ "role": {
56
+ "type": "string",
57
+ "enum": [
58
+ "tourist",
59
+ "resort",
60
+ "admin"
61
+ ]
37
62
  }
38
63
  }
39
64
  }
40
- },
41
- "400": { "$ref": "#/components/responses/BadRequest" }
65
+ }
66
+ }
67
+ },
68
+ "responses": {
69
+ "201": {
70
+ "description": "Usuario registrado"
42
71
  }
43
72
  }
44
- },
45
- "/experiences/{experienceId}": {
46
- "get": {
47
- "summary": "Detalle de experiencia",
48
- "operationId": "getExperience",
49
- "security": [],
50
- "parameters": [
51
- { "name": "experienceId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
52
- ],
53
- "responses": {
54
- "200": {
55
- "description": "OK",
56
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Experience" } } }
57
- },
58
- "400": { "$ref": "#/components/responses/BadRequest" },
59
- "404": { "$ref": "#/components/responses/NotFound" }
73
+ }
74
+ },
75
+ "/api/v1/auth/login": {
76
+ "post": {
77
+ "tags": [
78
+ "Auth"
79
+ ],
80
+ "summary": "Iniciar sesión",
81
+ "security": [],
82
+ "requestBody": {
83
+ "required": true,
84
+ "content": {
85
+ "application/json": {
86
+ "schema": {
87
+ "type": "object",
88
+ "required": [
89
+ "email",
90
+ "password"
91
+ ],
92
+ "properties": {
93
+ "email": {
94
+ "type": "string"
95
+ },
96
+ "password": {
97
+ "type": "string"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ },
104
+ "responses": {
105
+ "200": {
106
+ "description": "Login exitoso con tokens JWT"
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "/api/v1/auth/refresh": {
112
+ "post": {
113
+ "tags": [
114
+ "Auth"
115
+ ],
116
+ "summary": "Refrescar token",
117
+ "security": [],
118
+ "requestBody": {
119
+ "required": true,
120
+ "content": {
121
+ "application/json": {
122
+ "schema": {
123
+ "type": "object",
124
+ "required": [
125
+ "refreshToken"
126
+ ],
127
+ "properties": {
128
+ "refreshToken": {
129
+ "type": "string"
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ },
136
+ "responses": {
137
+ "200": {
138
+ "description": "Token renovado"
139
+ }
140
+ }
141
+ }
142
+ },
143
+ "/api/v1/auth/logout": {
144
+ "post": {
145
+ "tags": [
146
+ "Auth"
147
+ ],
148
+ "summary": "Cerrar sesión",
149
+ "requestBody": {
150
+ "content": {
151
+ "application/json": {
152
+ "schema": {
153
+ "type": "object",
154
+ "properties": {
155
+ "refreshToken": {
156
+ "type": "string"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ },
163
+ "responses": {
164
+ "200": {
165
+ "description": "Sesión cerrada"
166
+ }
167
+ }
168
+ }
169
+ },
170
+ "/api/v1/auth/password/request-reset": {
171
+ "post": {
172
+ "tags": [
173
+ "Auth"
174
+ ],
175
+ "summary": "Solicitar reseteo de contraseña",
176
+ "security": [],
177
+ "requestBody": {
178
+ "required": true,
179
+ "content": {
180
+ "application/json": {
181
+ "schema": {
182
+ "type": "object",
183
+ "required": [
184
+ "email"
185
+ ],
186
+ "properties": {
187
+ "email": {
188
+ "type": "string",
189
+ "format": "email"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ },
196
+ "responses": {
197
+ "200": {
198
+ "description": "Email enviado"
199
+ }
200
+ }
201
+ }
202
+ },
203
+ "/api/v1/auth/password/reset": {
204
+ "post": {
205
+ "tags": [
206
+ "Auth"
207
+ ],
208
+ "summary": "Resetear contraseña",
209
+ "security": [],
210
+ "requestBody": {
211
+ "required": true,
212
+ "content": {
213
+ "application/json": {
214
+ "schema": {
215
+ "type": "object",
216
+ "required": [
217
+ "token",
218
+ "newPassword"
219
+ ],
220
+ "properties": {
221
+ "token": {
222
+ "type": "string"
223
+ },
224
+ "newPassword": {
225
+ "type": "string",
226
+ "minLength": 8
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ },
233
+ "responses": {
234
+ "200": {
235
+ "description": "Contraseña actualizada"
236
+ }
237
+ }
238
+ }
239
+ },
240
+ "/api/v1/user": {
241
+ "get": {
242
+ "tags": [
243
+ "Users"
244
+ ],
245
+ "summary": "Obtener perfil del usuario autenticado",
246
+ "responses": {
247
+ "200": {
248
+ "description": "Perfil del usuario"
60
249
  }
61
250
  }
62
251
  },
63
- "/availability": {
64
- "get": {
65
- "summary": "Consultar disponibilidad por experiencia y fecha",
66
- "operationId": "getAvailability",
67
- "security": [],
68
- "parameters": [
69
- { "name": "experienceId", "in": "query", "required": true, "schema": { "type": "string", "format": "uuid" } },
70
- { "name": "date", "in": "query", "required": true, "schema": { "type": "string", "format": "date" } }
71
- ],
72
- "responses": {
73
- "200": {
74
- "description": "OK",
75
- "content": {
76
- "application/json": {
77
- "schema": { "type": "array", "items": { "$ref": "#/components/schemas/AvailabilitySlot" } }
252
+ "put": {
253
+ "tags": [
254
+ "Users"
255
+ ],
256
+ "summary": "Actualizar perfil",
257
+ "requestBody": {
258
+ "content": {
259
+ "application/json": {
260
+ "schema": {
261
+ "type": "object",
262
+ "properties": {
263
+ "fullName": {
264
+ "type": "string"
265
+ },
266
+ "email": {
267
+ "type": "string",
268
+ "format": "email"
269
+ }
78
270
  }
79
271
  }
80
- },
81
- "400": { "$ref": "#/components/responses/BadRequest" }
272
+ }
273
+ }
274
+ },
275
+ "responses": {
276
+ "200": {
277
+ "description": "Perfil actualizado"
278
+ }
279
+ }
280
+ }
281
+ },
282
+ "/api/v1/experiences": {
283
+ "get": {
284
+ "tags": [
285
+ "Experiences"
286
+ ],
287
+ "summary": "Listar experiencias",
288
+ "parameters": [
289
+ {
290
+ "name": "resort_id",
291
+ "in": "query",
292
+ "schema": {
293
+ "type": "string",
294
+ "format": "uuid"
295
+ }
296
+ },
297
+ {
298
+ "name": "category",
299
+ "in": "query",
300
+ "schema": {
301
+ "type": "string",
302
+ "enum": [
303
+ "islands",
304
+ "nautical",
305
+ "city_tour"
306
+ ]
307
+ }
308
+ },
309
+ {
310
+ "name": "status",
311
+ "in": "query",
312
+ "schema": {
313
+ "type": "string",
314
+ "enum": [
315
+ "draft",
316
+ "under_review",
317
+ "active",
318
+ "rejected"
319
+ ]
320
+ }
321
+ },
322
+ {
323
+ "name": "min_price",
324
+ "in": "query",
325
+ "schema": {
326
+ "type": "integer"
327
+ }
328
+ },
329
+ {
330
+ "name": "max_price",
331
+ "in": "query",
332
+ "schema": {
333
+ "type": "integer"
334
+ }
335
+ },
336
+ {
337
+ "name": "min_rating",
338
+ "in": "query",
339
+ "schema": {
340
+ "type": "number",
341
+ "minimum": 0,
342
+ "maximum": 5
343
+ }
344
+ },
345
+ {
346
+ "name": "has_images",
347
+ "in": "query",
348
+ "schema": {
349
+ "type": "boolean"
350
+ }
351
+ },
352
+ {
353
+ "name": "include_images",
354
+ "in": "query",
355
+ "schema": {
356
+ "type": "boolean"
357
+ }
358
+ },
359
+ {
360
+ "name": "limit",
361
+ "in": "query",
362
+ "schema": {
363
+ "type": "integer",
364
+ "default": 20,
365
+ "maximum": 100
366
+ }
367
+ },
368
+ {
369
+ "name": "offset",
370
+ "in": "query",
371
+ "schema": {
372
+ "type": "integer",
373
+ "default": 0
374
+ }
375
+ }
376
+ ],
377
+ "responses": {
378
+ "200": {
379
+ "description": "Lista paginada de experiencias"
82
380
  }
83
381
  }
84
382
  },
85
- "/bookings": {
86
- "post": {
87
- "summary": "Crear booking (pending + lock de inventario)",
88
- "operationId": "createBooking",
89
- "parameters": [
90
- {
91
- "name": "Idempotency-Key",
92
- "in": "header",
93
- "required": true,
94
- "description": "Requerido para evitar duplicados en creación.",
95
- "schema": { "type": "string", "maxLength": 128 }
96
- }
97
- ],
98
- "requestBody": {
99
- "required": true,
100
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBookingInput" } } }
101
- },
102
- "responses": {
103
- "201": {
104
- "description": "Creado",
105
- "headers": {
106
- "Location": { "schema": { "type": "string" }, "description": "URL del booking creado." }
107
- },
108
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } }
109
- },
110
- "400": { "$ref": "#/components/responses/BadRequest" },
111
- "409": { "$ref": "#/components/responses/Conflict" },
112
- "422": { "$ref": "#/components/responses/UnprocessableEntity" }
383
+ "post": {
384
+ "tags": [
385
+ "Experiences"
386
+ ],
387
+ "summary": "Crear experiencia",
388
+ "requestBody": {
389
+ "required": true,
390
+ "content": {
391
+ "application/json": {
392
+ "schema": {
393
+ "type": "object",
394
+ "required": [
395
+ "resort_id",
396
+ "title",
397
+ "category",
398
+ "price_cents"
399
+ ],
400
+ "properties": {
401
+ "resort_id": {
402
+ "type": "string",
403
+ "format": "uuid"
404
+ },
405
+ "title": {
406
+ "type": "string",
407
+ "minLength": 5,
408
+ "maxLength": 200
409
+ },
410
+ "description": {
411
+ "type": "string",
412
+ "maxLength": 2000
413
+ },
414
+ "category": {
415
+ "type": "string",
416
+ "enum": [
417
+ "islands",
418
+ "nautical",
419
+ "city_tour"
420
+ ]
421
+ },
422
+ "price_cents": {
423
+ "type": "integer",
424
+ "minimum": 1
425
+ },
426
+ "currency": {
427
+ "type": "string",
428
+ "default": "COP",
429
+ "maxLength": 3
430
+ },
431
+ "includes": {
432
+ "type": "string",
433
+ "maxLength": 1000
434
+ },
435
+ "excludes": {
436
+ "type": "string",
437
+ "maxLength": 1000
438
+ },
439
+ "main_image_url": {
440
+ "type": "string",
441
+ "format": "uri"
442
+ },
443
+ "status": {
444
+ "type": "string",
445
+ "enum": [
446
+ "draft",
447
+ "under_review",
448
+ "active",
449
+ "rejected"
450
+ ],
451
+ "default": "under_review"
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
457
+ },
458
+ "responses": {
459
+ "201": {
460
+ "description": "Experiencia creada"
461
+ }
462
+ }
463
+ }
464
+ },
465
+ "/api/v1/experiences/{id}": {
466
+ "get": {
467
+ "tags": [
468
+ "Experiences"
469
+ ],
470
+ "summary": "Obtener experiencia por ID",
471
+ "parameters": [
472
+ {
473
+ "name": "id",
474
+ "in": "path",
475
+ "required": true,
476
+ "schema": {
477
+ "type": "string",
478
+ "format": "uuid"
479
+ }
480
+ },
481
+ {
482
+ "name": "include_images",
483
+ "in": "query",
484
+ "schema": {
485
+ "type": "boolean"
486
+ }
487
+ }
488
+ ],
489
+ "responses": {
490
+ "200": {
491
+ "description": "Detalle de experiencia"
113
492
  }
114
493
  }
115
494
  },
116
- "/bookings/{bookingId}": {
117
- "get": {
118
- "summary": "Obtener booking por ID",
119
- "operationId": "getBooking",
120
- "parameters": [
121
- { "name": "bookingId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
122
- ],
123
- "responses": {
124
- "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Booking" } } } },
125
- "400": { "$ref": "#/components/responses/BadRequest" },
126
- "404": { "$ref": "#/components/responses/NotFound" }
495
+ "patch": {
496
+ "tags": [
497
+ "Experiences"
498
+ ],
499
+ "summary": "Actualizar experiencia",
500
+ "parameters": [
501
+ {
502
+ "name": "id",
503
+ "in": "path",
504
+ "required": true,
505
+ "schema": {
506
+ "type": "string",
507
+ "format": "uuid"
508
+ }
509
+ }
510
+ ],
511
+ "requestBody": {
512
+ "content": {
513
+ "application/json": {
514
+ "schema": {
515
+ "type": "object"
516
+ }
517
+ }
518
+ }
519
+ },
520
+ "responses": {
521
+ "200": {
522
+ "description": "Experiencia actualizada"
127
523
  }
128
524
  }
129
525
  },
130
- "/webhooks/payments/{provider}": {
131
- "post": {
132
- "summary": "Webhook de pagos (firma validada por API)",
133
- "operationId": "paymentsWebhook",
134
- "security": [],
135
- "parameters": [
136
- { "name": "provider", "in": "path", "required": true, "schema": { "type": "string", "enum": ["wompi", "epayco", "other"] } }
137
- ],
138
- "requestBody": {
526
+ "delete": {
527
+ "tags": [
528
+ "Experiences"
529
+ ],
530
+ "summary": "Eliminar experiencia",
531
+ "parameters": [
532
+ {
533
+ "name": "id",
534
+ "in": "path",
139
535
  "required": true,
140
- "content": {
141
- "application/json": { "schema": { "$ref": "#/components/schemas/PaymentWebhookPayload" } }
536
+ "schema": {
537
+ "type": "string",
538
+ "format": "uuid"
539
+ }
540
+ }
541
+ ],
542
+ "responses": {
543
+ "204": {
544
+ "description": "Eliminada"
545
+ }
546
+ }
547
+ }
548
+ },
549
+ "/api/v1/experiences/resort/{resortId}/slug/{slug}": {
550
+ "get": {
551
+ "tags": [
552
+ "Experiences"
553
+ ],
554
+ "summary": "Buscar experiencia por slug y resort",
555
+ "parameters": [
556
+ {
557
+ "name": "resortId",
558
+ "in": "path",
559
+ "required": true,
560
+ "schema": {
561
+ "type": "string",
562
+ "format": "uuid"
563
+ }
564
+ },
565
+ {
566
+ "name": "slug",
567
+ "in": "path",
568
+ "required": true,
569
+ "schema": {
570
+ "type": "string"
142
571
  }
143
572
  },
144
- "responses": {
145
- "202": { "description": "Aceptado para procesamiento asíncrono" },
146
- "400": { "$ref": "#/components/responses/BadRequest" },
147
- "401": { "$ref": "#/components/responses/Unauthorized" }
573
+ {
574
+ "name": "include_images",
575
+ "in": "query",
576
+ "schema": {
577
+ "type": "boolean"
578
+ }
579
+ }
580
+ ],
581
+ "responses": {
582
+ "200": {
583
+ "description": "Experiencia encontrada"
584
+ }
585
+ }
586
+ }
587
+ },
588
+ "/api/v1/experiences/{id}/images/presign": {
589
+ "post": {
590
+ "tags": [
591
+ "Experiences"
592
+ ],
593
+ "summary": "Obtener URL pre-firmada para subir imagen",
594
+ "parameters": [
595
+ {
596
+ "name": "id",
597
+ "in": "path",
598
+ "required": true,
599
+ "schema": {
600
+ "type": "string",
601
+ "format": "uuid"
602
+ }
603
+ }
604
+ ],
605
+ "requestBody": {
606
+ "required": true,
607
+ "content": {
608
+ "application/json": {
609
+ "schema": {
610
+ "type": "object",
611
+ "required": [
612
+ "filename",
613
+ "contentType"
614
+ ],
615
+ "properties": {
616
+ "filename": {
617
+ "type": "string"
618
+ },
619
+ "contentType": {
620
+ "type": "string"
621
+ }
622
+ }
623
+ }
624
+ }
625
+ }
626
+ },
627
+ "responses": {
628
+ "201": {
629
+ "description": "URL pre-firmada generada"
148
630
  }
149
631
  }
150
632
  }
151
633
  },
152
- "components": {
153
- "securitySchemes": {
154
- "BearerAuth": {
155
- "type": "http",
156
- "scheme": "bearer",
157
- "bearerFormat": "JWT"
634
+ "/api/v1/experiences/{id}/images/upload": {
635
+ "post": {
636
+ "tags": [
637
+ "Experiences"
638
+ ],
639
+ "summary": "Subir imagen de experiencia",
640
+ "parameters": [
641
+ {
642
+ "name": "id",
643
+ "in": "path",
644
+ "required": true,
645
+ "schema": {
646
+ "type": "string",
647
+ "format": "uuid"
648
+ }
649
+ }
650
+ ],
651
+ "requestBody": {
652
+ "required": true,
653
+ "content": {
654
+ "multipart/form-data": {
655
+ "schema": {
656
+ "type": "object"
657
+ }
658
+ }
659
+ }
660
+ },
661
+ "responses": {
662
+ "201": {
663
+ "description": "Imagen subida"
664
+ }
158
665
  }
159
- },
160
- "schemas": {
161
- "UUID": { "type": "string", "format": "uuid" },
162
- "Money": {
163
- "type": "object",
164
- "properties": {
165
- "amount": { "type": "integer", "minimum": 0, "description": "Centavos" },
166
- "currency": { "type": "string", "minLength": 3, "maxLength": 3, "example": "COP" }
167
- },
168
- "required": ["amount", "currency"],
169
- "additionalProperties": false
170
- },
171
- "ExperienceSummary": {
172
- "type": "object",
173
- "properties": {
174
- "id": { "$ref": "#/components/schemas/UUID" },
175
- "title": { "type": "string" },
176
- "coverImageUrl": { "type": "string", "format": "uri" },
177
- "city": { "type": "string" },
178
- "country": { "type": "string" },
179
- "ratingAvg": { "type": "number", "minimum": 0, "maximum": 5 },
180
- "ratingCount": { "type": "integer", "minimum": 0 },
181
- "fromPrice": { "$ref": "#/components/schemas/Money" }
182
- },
183
- "required": ["id", "title", "fromPrice"]
184
- },
185
- "Experience": {
186
- "allOf": [
187
- { "$ref": "#/components/schemas/ExperienceSummary" },
188
- {
189
- "type": "object",
190
- "properties": {
191
- "description": { "type": "string" },
192
- "includes": { "type": "array", "items": { "type": "string" } },
193
- "notIncludes": { "type": "array", "items": { "type": "string" } },
194
- "images": { "type": "array", "items": { "type": "string", "format": "uri" } },
195
- "status": { "type": "string", "enum": ["draft", "under_review", "active", "rejected"] }
196
- },
197
- "required": ["status"]
198
- }
199
- ]
200
- },
201
- "AvailabilitySlot": {
202
- "type": "object",
203
- "properties": {
204
- "slotId": { "$ref": "#/components/schemas/UUID" },
205
- "start": { "type": "string", "format": "date-time" },
206
- "end": { "type": "string", "format": "date-time" },
207
- "capacity": { "type": "integer", "minimum": 1 },
208
- "remaining": { "type": "integer", "minimum": 0 },
209
- "price": { "$ref": "#/components/schemas/Money" }
210
- },
211
- "required": ["slotId", "start", "end", "capacity", "remaining", "price"]
212
- },
213
- "CreateBookingInput": {
214
- "type": "object",
215
- "properties": {
216
- "experienceId": { "$ref": "#/components/schemas/UUID" },
217
- "slotId": { "$ref": "#/components/schemas/UUID" },
218
- "quantity": { "type": "integer", "minimum": 1 },
219
- "buyer": {
220
- "type": "object",
221
- "properties": {
222
- "fullName": { "type": "string" },
223
- "email": { "type": "string", "format": "email" },
224
- "phone": { "type": "string" }
225
- },
226
- "required": ["fullName", "email"]
227
- },
228
- "payment": {
229
- "type": "object",
230
- "properties": {
231
- "method": { "type": "string", "enum": ["card", "pse", "cash", "other"] },
232
- "provider": { "type": "string", "enum": ["wompi", "epayco", "other"] }
233
- },
234
- "required": ["method", "provider"]
235
- }
236
- },
237
- "required": ["experienceId", "slotId", "quantity", "buyer", "payment"],
238
- "additionalProperties": false
239
- },
240
- "Booking": {
241
- "type": "object",
242
- "properties": {
243
- "id": { "$ref": "#/components/schemas/UUID" },
244
- "status": { "type": "string", "enum": ["pending", "confirmed", "completed", "cancelled", "refunded", "expired"] },
245
- "experienceId": { "$ref": "#/components/schemas/UUID" },
246
- "slotId": { "$ref": "#/components/schemas/UUID" },
247
- "quantity": { "type": "integer", "minimum": 1 },
248
- "total": { "$ref": "#/components/schemas/Money" },
249
- "createdAt": { "type": "string", "format": "date-time" }
250
- },
251
- "required": ["id", "status", "experienceId", "slotId", "quantity", "total", "createdAt"]
252
- },
253
- "PaymentWebhookPayload": {
254
- "type": "object",
255
- "properties": {
256
- "event": { "type": "string", "example": "transaction.updated" },
257
- "data": {
258
- "type": "object",
259
- "properties": {
260
- "providerRef": { "type": "string" },
261
- "status": { "type": "string", "enum": ["APPROVED", "DECLINED", "VOIDED", "ERROR", "PENDING"] },
262
- "amountInCents": { "type": "integer", "minimum": 0 },
263
- "currency": { "type": "string", "minLength": 3, "maxLength": 3 },
264
- "bookingId": { "type": "string", "format": "uuid", "description": "Opcional si el flujo incluye referencia directa al booking." }
265
- },
266
- "required": ["providerRef", "status", "amountInCents", "currency"]
267
- },
268
- "sentAt": { "type": "string", "format": "date-time" }
666
+ }
667
+ },
668
+ "/api/v1/experiences/{id}/images/{imageId}": {
669
+ "delete": {
670
+ "tags": [
671
+ "Experiences"
672
+ ],
673
+ "summary": "Eliminar imagen de experiencia",
674
+ "parameters": [
675
+ {
676
+ "name": "id",
677
+ "in": "path",
678
+ "required": true,
679
+ "schema": {
680
+ "type": "string",
681
+ "format": "uuid"
682
+ }
269
683
  },
270
- "required": ["event", "data"]
684
+ {
685
+ "name": "imageId",
686
+ "in": "path",
687
+ "required": true,
688
+ "schema": {
689
+ "type": "string",
690
+ "format": "uuid"
691
+ }
692
+ }
693
+ ],
694
+ "responses": {
695
+ "204": {
696
+ "description": "Imagen eliminada"
697
+ }
698
+ }
699
+ }
700
+ },
701
+ "/api/v1/experiences/{id}/submit": {
702
+ "post": {
703
+ "tags": [
704
+ "Experiences"
705
+ ],
706
+ "summary": "Enviar experiencia a revisión",
707
+ "parameters": [
708
+ {
709
+ "name": "id",
710
+ "in": "path",
711
+ "required": true,
712
+ "schema": {
713
+ "type": "string",
714
+ "format": "uuid"
715
+ }
716
+ }
717
+ ],
718
+ "requestBody": {
719
+ "content": {
720
+ "application/json": {
721
+ "schema": {
722
+ "type": "object",
723
+ "properties": {
724
+ "notes": {
725
+ "type": "string"
726
+ }
727
+ }
728
+ }
729
+ }
730
+ }
271
731
  },
272
- "Error": {
273
- "type": "object",
274
- "properties": {
275
- "error_code": { "type": "string" },
276
- "message": { "type": "string" },
277
- "details": { "type": ["object", "null"] }
732
+ "responses": {
733
+ "200": {
734
+ "description": "Enviada a revisión"
735
+ }
736
+ }
737
+ }
738
+ },
739
+ "/api/v1/categories": {
740
+ "get": {
741
+ "tags": [
742
+ "Categories"
743
+ ],
744
+ "summary": "Listar categorías con paginación y filtros",
745
+ "parameters": [
746
+ {
747
+ "name": "name",
748
+ "in": "query",
749
+ "schema": {
750
+ "type": "string"
751
+ }
752
+ },
753
+ {
754
+ "name": "slug",
755
+ "in": "query",
756
+ "schema": {
757
+ "type": "string"
758
+ }
278
759
  },
279
- "required": ["error_code", "message"]
760
+ {
761
+ "name": "limit",
762
+ "in": "query",
763
+ "schema": {
764
+ "type": "integer",
765
+ "default": 20,
766
+ "minimum": 1,
767
+ "maximum": 100
768
+ }
769
+ },
770
+ {
771
+ "name": "offset",
772
+ "in": "query",
773
+ "schema": {
774
+ "type": "integer",
775
+ "default": 0,
776
+ "minimum": 0
777
+ }
778
+ }
779
+ ],
780
+ "responses": {
781
+ "200": {
782
+ "description": "Lista paginada de categorías",
783
+ "content": {
784
+ "application/json": {
785
+ "schema": {
786
+ "type": "object",
787
+ "properties": {
788
+ "items": {
789
+ "type": "array",
790
+ "items": {
791
+ "type": "object",
792
+ "properties": {
793
+ "id": {
794
+ "type": "string",
795
+ "format": "uuid"
796
+ },
797
+ "name": {
798
+ "type": "string"
799
+ },
800
+ "slug": {
801
+ "type": "string"
802
+ },
803
+ "created_at": {
804
+ "type": "string",
805
+ "format": "date-time"
806
+ },
807
+ "updated_at": {
808
+ "type": "string",
809
+ "format": "date-time"
810
+ }
811
+ }
812
+ }
813
+ },
814
+ "total": {
815
+ "type": "integer"
816
+ },
817
+ "limit": {
818
+ "type": "integer"
819
+ },
820
+ "offset": {
821
+ "type": "integer"
822
+ }
823
+ }
824
+ }
825
+ }
826
+ }
827
+ }
280
828
  }
281
829
  },
282
- "responses": {
283
- "BadRequest": {
284
- "description": "Solicitud inválida",
285
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
286
- },
287
- "Unauthorized": { "description": "No autorizado" },
288
- "NotFound": {
289
- "description": "No encontrado",
290
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
830
+ "post": {
831
+ "tags": [
832
+ "Categories"
833
+ ],
834
+ "summary": "Crear nueva categoría",
835
+ "requestBody": {
836
+ "required": true,
837
+ "content": {
838
+ "application/json": {
839
+ "schema": {
840
+ "type": "object",
841
+ "required": [
842
+ "name"
843
+ ],
844
+ "properties": {
845
+ "name": {
846
+ "type": "string",
847
+ "minLength": 2,
848
+ "maxLength": 100
849
+ },
850
+ "slug": {
851
+ "type": "string",
852
+ "minLength": 2,
853
+ "maxLength": 100,
854
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
855
+ }
856
+ }
857
+ }
858
+ }
859
+ }
291
860
  },
292
- "Conflict": {
293
- "description": "Conflicto (idempotencia, duplicado, etc.)",
294
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
861
+ "responses": {
862
+ "201": {
863
+ "description": "Categoría creada exitosamente",
864
+ "content": {
865
+ "application/json": {
866
+ "schema": {
867
+ "type": "object",
868
+ "properties": {
869
+ "id": {
870
+ "type": "string",
871
+ "format": "uuid"
872
+ },
873
+ "name": {
874
+ "type": "string"
875
+ },
876
+ "slug": {
877
+ "type": "string"
878
+ },
879
+ "created_at": {
880
+ "type": "string",
881
+ "format": "date-time"
882
+ },
883
+ "updated_at": {
884
+ "type": "string",
885
+ "format": "date-time"
886
+ }
887
+ }
888
+ }
889
+ }
890
+ }
891
+ }
892
+ }
893
+ }
894
+ },
895
+ "/api/v1/categories/{id}": {
896
+ "get": {
897
+ "tags": [
898
+ "Categories"
899
+ ],
900
+ "summary": "Obtener categoría por ID",
901
+ "parameters": [
902
+ {
903
+ "name": "id",
904
+ "in": "path",
905
+ "required": true,
906
+ "schema": {
907
+ "type": "string",
908
+ "format": "uuid"
909
+ }
910
+ }
911
+ ],
912
+ "responses": {
913
+ "200": {
914
+ "description": "Categoría encontrada",
915
+ "content": {
916
+ "application/json": {
917
+ "schema": {
918
+ "type": "object",
919
+ "properties": {
920
+ "id": {
921
+ "type": "string",
922
+ "format": "uuid"
923
+ },
924
+ "name": {
925
+ "type": "string"
926
+ },
927
+ "slug": {
928
+ "type": "string"
929
+ },
930
+ "created_at": {
931
+ "type": "string",
932
+ "format": "date-time"
933
+ },
934
+ "updated_at": {
935
+ "type": "string",
936
+ "format": "date-time"
937
+ }
938
+ }
939
+ }
940
+ }
941
+ }
942
+ }
943
+ }
944
+ },
945
+ "patch": {
946
+ "tags": [
947
+ "Categories"
948
+ ],
949
+ "summary": "Actualizar categoría",
950
+ "parameters": [
951
+ {
952
+ "name": "id",
953
+ "in": "path",
954
+ "required": true,
955
+ "schema": {
956
+ "type": "string",
957
+ "format": "uuid"
958
+ }
959
+ }
960
+ ],
961
+ "requestBody": {
962
+ "required": true,
963
+ "content": {
964
+ "application/json": {
965
+ "schema": {
966
+ "type": "object",
967
+ "properties": {
968
+ "name": {
969
+ "type": "string",
970
+ "minLength": 2,
971
+ "maxLength": 100
972
+ },
973
+ "slug": {
974
+ "type": "string",
975
+ "minLength": 2,
976
+ "maxLength": 100,
977
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
978
+ }
979
+ }
980
+ }
981
+ }
982
+ }
295
983
  },
296
- "UnprocessableEntity": {
297
- "description": "Validación fallida",
298
- "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
984
+ "responses": {
985
+ "200": {
986
+ "description": "Categoría actualizada",
987
+ "content": {
988
+ "application/json": {
989
+ "schema": {
990
+ "type": "object",
991
+ "properties": {
992
+ "id": {
993
+ "type": "string",
994
+ "format": "uuid"
995
+ },
996
+ "name": {
997
+ "type": "string"
998
+ },
999
+ "slug": {
1000
+ "type": "string"
1001
+ },
1002
+ "created_at": {
1003
+ "type": "string",
1004
+ "format": "date-time"
1005
+ },
1006
+ "updated_at": {
1007
+ "type": "string",
1008
+ "format": "date-time"
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ }
1016
+ },
1017
+ "delete": {
1018
+ "tags": [
1019
+ "Categories"
1020
+ ],
1021
+ "summary": "Eliminar categoría",
1022
+ "parameters": [
1023
+ {
1024
+ "name": "id",
1025
+ "in": "path",
1026
+ "required": true,
1027
+ "schema": {
1028
+ "type": "string",
1029
+ "format": "uuid"
1030
+ }
1031
+ }
1032
+ ],
1033
+ "responses": {
1034
+ "204": {
1035
+ "description": "Categoría eliminada exitosamente"
1036
+ }
299
1037
  }
300
1038
  }
1039
+ },
1040
+ "/api/v1/categories/slug/{slug}": {
1041
+ "get": {
1042
+ "tags": [
1043
+ "Categories"
1044
+ ],
1045
+ "summary": "Buscar categoría por slug",
1046
+ "parameters": [
1047
+ {
1048
+ "name": "slug",
1049
+ "in": "path",
1050
+ "required": true,
1051
+ "schema": {
1052
+ "type": "string",
1053
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
1054
+ }
1055
+ }
1056
+ ],
1057
+ "responses": {
1058
+ "200": {
1059
+ "description": "Categoría encontrada",
1060
+ "content": {
1061
+ "application/json": {
1062
+ "schema": {
1063
+ "type": "object",
1064
+ "properties": {
1065
+ "id": {
1066
+ "type": "string",
1067
+ "format": "uuid"
1068
+ },
1069
+ "name": {
1070
+ "type": "string"
1071
+ },
1072
+ "slug": {
1073
+ "type": "string"
1074
+ },
1075
+ "created_at": {
1076
+ "type": "string",
1077
+ "format": "date-time"
1078
+ },
1079
+ "updated_at": {
1080
+ "type": "string",
1081
+ "format": "date-time"
1082
+ }
1083
+ }
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ }
1089
+ }
1090
+ },
1091
+ "/api/v1/resorts": {
1092
+ "get": {
1093
+ "tags": [
1094
+ "Resorts"
1095
+ ],
1096
+ "summary": "Listar todos los resorts (Admin)",
1097
+ "parameters": [
1098
+ {
1099
+ "name": "limit",
1100
+ "in": "query",
1101
+ "schema": {
1102
+ "type": "integer",
1103
+ "default": 20,
1104
+ "minimum": 1,
1105
+ "maximum": 100
1106
+ }
1107
+ },
1108
+ {
1109
+ "name": "offset",
1110
+ "in": "query",
1111
+ "schema": {
1112
+ "type": "integer",
1113
+ "default": 0,
1114
+ "minimum": 0
1115
+ }
1116
+ }
1117
+ ],
1118
+ "responses": {
1119
+ "200": {
1120
+ "description": "Lista paginada de resorts",
1121
+ "content": {
1122
+ "application/json": {
1123
+ "schema": {
1124
+ "type": "object",
1125
+ "properties": {
1126
+ "items": {
1127
+ "type": "array",
1128
+ "items": {
1129
+ "type": "object",
1130
+ "properties": {
1131
+ "id": {
1132
+ "type": "string",
1133
+ "format": "uuid"
1134
+ },
1135
+ "owner_id": {
1136
+ "type": "string",
1137
+ "format": "uuid"
1138
+ },
1139
+ "name": {
1140
+ "type": "string"
1141
+ },
1142
+ "description": {
1143
+ "type": "string"
1144
+ },
1145
+ "contact_email": {
1146
+ "type": "string",
1147
+ "format": "email"
1148
+ },
1149
+ "contact_phone": {
1150
+ "type": "string"
1151
+ },
1152
+ "address_line": {
1153
+ "type": "string"
1154
+ },
1155
+ "city": {
1156
+ "type": "string"
1157
+ },
1158
+ "country": {
1159
+ "type": "string"
1160
+ },
1161
+ "latitude": {
1162
+ "type": "number"
1163
+ },
1164
+ "longitude": {
1165
+ "type": "number"
1166
+ },
1167
+ "is_active": {
1168
+ "type": "boolean"
1169
+ },
1170
+ "status": {
1171
+ "type": "string",
1172
+ "enum": [
1173
+ "draft",
1174
+ "under_review",
1175
+ "active",
1176
+ "rejected"
1177
+ ]
1178
+ },
1179
+ "created_at": {
1180
+ "type": "string",
1181
+ "format": "date-time"
1182
+ },
1183
+ "updated_at": {
1184
+ "type": "string",
1185
+ "format": "date-time"
1186
+ }
1187
+ }
1188
+ }
1189
+ },
1190
+ "total": {
1191
+ "type": "integer"
1192
+ },
1193
+ "limit": {
1194
+ "type": "integer"
1195
+ },
1196
+ "offset": {
1197
+ "type": "integer"
1198
+ }
1199
+ }
1200
+ }
1201
+ }
1202
+ }
1203
+ }
1204
+ }
1205
+ },
1206
+ "post": {
1207
+ "tags": [
1208
+ "Resorts"
1209
+ ],
1210
+ "summary": "Crear resort",
1211
+ "requestBody": {
1212
+ "required": true,
1213
+ "content": {
1214
+ "application/json": {
1215
+ "schema": {
1216
+ "type": "object",
1217
+ "required": [
1218
+ "name"
1219
+ ],
1220
+ "properties": {
1221
+ "name": {
1222
+ "type": "string",
1223
+ "minLength": 2,
1224
+ "maxLength": 100
1225
+ },
1226
+ "description": {
1227
+ "type": "string",
1228
+ "maxLength": 1000
1229
+ },
1230
+ "contact_email": {
1231
+ "type": "string",
1232
+ "format": "email"
1233
+ },
1234
+ "contact_phone": {
1235
+ "type": "string",
1236
+ "maxLength": 20
1237
+ },
1238
+ "address_line": {
1239
+ "type": "string",
1240
+ "maxLength": 200
1241
+ },
1242
+ "city": {
1243
+ "type": "string",
1244
+ "maxLength": 100
1245
+ },
1246
+ "country": {
1247
+ "type": "string",
1248
+ "maxLength": 100
1249
+ },
1250
+ "latitude": {
1251
+ "type": "number"
1252
+ },
1253
+ "longitude": {
1254
+ "type": "number"
1255
+ },
1256
+ "is_active": {
1257
+ "type": "boolean"
1258
+ }
1259
+ }
1260
+ }
1261
+ }
1262
+ }
1263
+ },
1264
+ "responses": {
1265
+ "201": {
1266
+ "description": "Resort creado",
1267
+ "content": {
1268
+ "application/json": {
1269
+ "schema": {
1270
+ "type": "object",
1271
+ "properties": {
1272
+ "id": {
1273
+ "type": "string",
1274
+ "format": "uuid"
1275
+ },
1276
+ "owner_id": {
1277
+ "type": "string",
1278
+ "format": "uuid"
1279
+ },
1280
+ "name": {
1281
+ "type": "string"
1282
+ },
1283
+ "description": {
1284
+ "type": "string"
1285
+ },
1286
+ "contact_email": {
1287
+ "type": "string"
1288
+ },
1289
+ "contact_phone": {
1290
+ "type": "string"
1291
+ },
1292
+ "address_line": {
1293
+ "type": "string"
1294
+ },
1295
+ "city": {
1296
+ "type": "string"
1297
+ },
1298
+ "country": {
1299
+ "type": "string"
1300
+ },
1301
+ "latitude": {
1302
+ "type": "number"
1303
+ },
1304
+ "longitude": {
1305
+ "type": "number"
1306
+ },
1307
+ "is_active": {
1308
+ "type": "boolean"
1309
+ },
1310
+ "status": {
1311
+ "type": "string"
1312
+ },
1313
+ "created_at": {
1314
+ "type": "string",
1315
+ "format": "date-time"
1316
+ },
1317
+ "updated_at": {
1318
+ "type": "string",
1319
+ "format": "date-time"
1320
+ }
1321
+ }
1322
+ }
1323
+ }
1324
+ }
1325
+ }
1326
+ }
1327
+ }
1328
+ },
1329
+ "/api/v1/resorts/my-resorts": {
1330
+ "get": {
1331
+ "tags": [
1332
+ "Resorts"
1333
+ ],
1334
+ "summary": "Obtener mis resorts",
1335
+ "parameters": [
1336
+ {
1337
+ "name": "limit",
1338
+ "in": "query",
1339
+ "schema": {
1340
+ "type": "integer",
1341
+ "default": 20
1342
+ }
1343
+ },
1344
+ {
1345
+ "name": "offset",
1346
+ "in": "query",
1347
+ "schema": {
1348
+ "type": "integer",
1349
+ "default": 0
1350
+ }
1351
+ }
1352
+ ],
1353
+ "responses": {
1354
+ "200": {
1355
+ "description": "Lista de mis resorts",
1356
+ "content": {
1357
+ "application/json": {
1358
+ "schema": {
1359
+ "type": "object",
1360
+ "properties": {
1361
+ "items": {
1362
+ "type": "array",
1363
+ "items": {
1364
+ "type": "object",
1365
+ "properties": {
1366
+ "id": {
1367
+ "type": "string",
1368
+ "format": "uuid"
1369
+ },
1370
+ "name": {
1371
+ "type": "string"
1372
+ },
1373
+ "description": {
1374
+ "type": "string"
1375
+ },
1376
+ "city": {
1377
+ "type": "string"
1378
+ },
1379
+ "country": {
1380
+ "type": "string"
1381
+ },
1382
+ "status": {
1383
+ "type": "string"
1384
+ },
1385
+ "created_at": {
1386
+ "type": "string",
1387
+ "format": "date-time"
1388
+ }
1389
+ }
1390
+ }
1391
+ },
1392
+ "total": {
1393
+ "type": "integer"
1394
+ }
1395
+ }
1396
+ }
1397
+ }
1398
+ }
1399
+ }
1400
+ }
1401
+ }
1402
+ },
1403
+ "/api/v1/resorts/{id}": {
1404
+ "get": {
1405
+ "tags": [
1406
+ "Resorts"
1407
+ ],
1408
+ "summary": "Obtener resort por ID",
1409
+ "parameters": [
1410
+ {
1411
+ "name": "id",
1412
+ "in": "path",
1413
+ "required": true,
1414
+ "schema": {
1415
+ "type": "string",
1416
+ "format": "uuid"
1417
+ }
1418
+ }
1419
+ ],
1420
+ "responses": {
1421
+ "200": {
1422
+ "description": "Resort encontrado",
1423
+ "content": {
1424
+ "application/json": {
1425
+ "schema": {
1426
+ "type": "object",
1427
+ "properties": {
1428
+ "id": {
1429
+ "type": "string",
1430
+ "format": "uuid"
1431
+ },
1432
+ "owner_id": {
1433
+ "type": "string",
1434
+ "format": "uuid"
1435
+ },
1436
+ "name": {
1437
+ "type": "string"
1438
+ },
1439
+ "description": {
1440
+ "type": "string"
1441
+ },
1442
+ "contact_email": {
1443
+ "type": "string"
1444
+ },
1445
+ "contact_phone": {
1446
+ "type": "string"
1447
+ },
1448
+ "address_line": {
1449
+ "type": "string"
1450
+ },
1451
+ "city": {
1452
+ "type": "string"
1453
+ },
1454
+ "country": {
1455
+ "type": "string"
1456
+ },
1457
+ "latitude": {
1458
+ "type": "number"
1459
+ },
1460
+ "longitude": {
1461
+ "type": "number"
1462
+ },
1463
+ "is_active": {
1464
+ "type": "boolean"
1465
+ },
1466
+ "status": {
1467
+ "type": "string"
1468
+ },
1469
+ "created_at": {
1470
+ "type": "string",
1471
+ "format": "date-time"
1472
+ },
1473
+ "updated_at": {
1474
+ "type": "string",
1475
+ "format": "date-time"
1476
+ }
1477
+ }
1478
+ }
1479
+ }
1480
+ }
1481
+ }
1482
+ }
1483
+ },
1484
+ "patch": {
1485
+ "tags": [
1486
+ "Resorts"
1487
+ ],
1488
+ "summary": "Actualizar resort",
1489
+ "parameters": [
1490
+ {
1491
+ "name": "id",
1492
+ "in": "path",
1493
+ "required": true,
1494
+ "schema": {
1495
+ "type": "string",
1496
+ "format": "uuid"
1497
+ }
1498
+ }
1499
+ ],
1500
+ "requestBody": {
1501
+ "required": true,
1502
+ "content": {
1503
+ "application/json": {
1504
+ "schema": {
1505
+ "type": "object",
1506
+ "properties": {
1507
+ "name": {
1508
+ "type": "string",
1509
+ "minLength": 2,
1510
+ "maxLength": 100
1511
+ },
1512
+ "description": {
1513
+ "type": "string",
1514
+ "maxLength": 1000
1515
+ },
1516
+ "contact_email": {
1517
+ "type": "string",
1518
+ "format": "email"
1519
+ },
1520
+ "contact_phone": {
1521
+ "type": "string",
1522
+ "maxLength": 20
1523
+ },
1524
+ "address_line": {
1525
+ "type": "string",
1526
+ "maxLength": 200
1527
+ },
1528
+ "city": {
1529
+ "type": "string",
1530
+ "maxLength": 100
1531
+ },
1532
+ "country": {
1533
+ "type": "string",
1534
+ "maxLength": 100
1535
+ },
1536
+ "latitude": {
1537
+ "type": "number"
1538
+ },
1539
+ "longitude": {
1540
+ "type": "number"
1541
+ },
1542
+ "is_active": {
1543
+ "type": "boolean"
1544
+ }
1545
+ }
1546
+ }
1547
+ }
1548
+ }
1549
+ },
1550
+ "responses": {
1551
+ "200": {
1552
+ "description": "Resort actualizado",
1553
+ "content": {
1554
+ "application/json": {
1555
+ "schema": {
1556
+ "type": "object",
1557
+ "properties": {
1558
+ "id": {
1559
+ "type": "string",
1560
+ "format": "uuid"
1561
+ },
1562
+ "name": {
1563
+ "type": "string"
1564
+ },
1565
+ "description": {
1566
+ "type": "string"
1567
+ },
1568
+ "status": {
1569
+ "type": "string"
1570
+ },
1571
+ "updated_at": {
1572
+ "type": "string",
1573
+ "format": "date-time"
1574
+ }
1575
+ }
1576
+ }
1577
+ }
1578
+ }
1579
+ }
1580
+ }
1581
+ },
1582
+ "delete": {
1583
+ "tags": [
1584
+ "Resorts"
1585
+ ],
1586
+ "summary": "Eliminar resort",
1587
+ "parameters": [
1588
+ {
1589
+ "name": "id",
1590
+ "in": "path",
1591
+ "required": true,
1592
+ "schema": {
1593
+ "type": "string",
1594
+ "format": "uuid"
1595
+ }
1596
+ }
1597
+ ],
1598
+ "responses": {
1599
+ "204": {
1600
+ "description": "Resort eliminado exitosamente"
1601
+ }
1602
+ }
1603
+ }
1604
+ },
1605
+ "/api/v1/resorts/{id}/submit": {
1606
+ "post": {
1607
+ "tags": [
1608
+ "Resorts"
1609
+ ],
1610
+ "summary": "Enviar resort a revisión",
1611
+ "parameters": [
1612
+ {
1613
+ "name": "id",
1614
+ "in": "path",
1615
+ "required": true,
1616
+ "schema": {
1617
+ "type": "string",
1618
+ "format": "uuid"
1619
+ }
1620
+ }
1621
+ ],
1622
+ "responses": {
1623
+ "200": {
1624
+ "description": "Resort enviado a revisión",
1625
+ "content": {
1626
+ "application/json": {
1627
+ "schema": {
1628
+ "type": "object",
1629
+ "properties": {
1630
+ "id": {
1631
+ "type": "string",
1632
+ "format": "uuid"
1633
+ },
1634
+ "name": {
1635
+ "type": "string"
1636
+ },
1637
+ "status": {
1638
+ "type": "string",
1639
+ "enum": [
1640
+ "under_review"
1641
+ ]
1642
+ },
1643
+ "updated_at": {
1644
+ "type": "string",
1645
+ "format": "date-time"
1646
+ }
1647
+ }
1648
+ }
1649
+ }
1650
+ }
1651
+ }
1652
+ }
1653
+ }
1654
+ },
1655
+ "/api/v1/resorts/{id}/approve": {
1656
+ "post": {
1657
+ "tags": [
1658
+ "Resorts"
1659
+ ],
1660
+ "summary": "Aprobar resort (Admin)",
1661
+ "parameters": [
1662
+ {
1663
+ "name": "id",
1664
+ "in": "path",
1665
+ "required": true,
1666
+ "schema": {
1667
+ "type": "string",
1668
+ "format": "uuid"
1669
+ }
1670
+ }
1671
+ ],
1672
+ "requestBody": {
1673
+ "content": {
1674
+ "application/json": {
1675
+ "schema": {
1676
+ "type": "object",
1677
+ "properties": {
1678
+ "notes": {
1679
+ "type": "string",
1680
+ "maxLength": 500
1681
+ }
1682
+ }
1683
+ }
1684
+ }
1685
+ }
1686
+ },
1687
+ "responses": {
1688
+ "200": {
1689
+ "description": "Resort aprobado",
1690
+ "content": {
1691
+ "application/json": {
1692
+ "schema": {
1693
+ "type": "object",
1694
+ "properties": {
1695
+ "id": {
1696
+ "type": "string",
1697
+ "format": "uuid"
1698
+ },
1699
+ "name": {
1700
+ "type": "string"
1701
+ },
1702
+ "status": {
1703
+ "type": "string",
1704
+ "enum": [
1705
+ "active"
1706
+ ]
1707
+ },
1708
+ "updated_at": {
1709
+ "type": "string",
1710
+ "format": "date-time"
1711
+ }
1712
+ }
1713
+ }
1714
+ }
1715
+ }
1716
+ }
1717
+ }
1718
+ }
1719
+ },
1720
+ "/api/v1/resorts/{id}/reject": {
1721
+ "post": {
1722
+ "tags": [
1723
+ "Resorts"
1724
+ ],
1725
+ "summary": "Rechazar resort (Admin)",
1726
+ "parameters": [
1727
+ {
1728
+ "name": "id",
1729
+ "in": "path",
1730
+ "required": true,
1731
+ "schema": {
1732
+ "type": "string",
1733
+ "format": "uuid"
1734
+ }
1735
+ }
1736
+ ],
1737
+ "requestBody": {
1738
+ "required": true,
1739
+ "content": {
1740
+ "application/json": {
1741
+ "schema": {
1742
+ "type": "object",
1743
+ "required": [
1744
+ "rejection_reason"
1745
+ ],
1746
+ "properties": {
1747
+ "rejection_reason": {
1748
+ "type": "string",
1749
+ "maxLength": 500
1750
+ }
1751
+ }
1752
+ }
1753
+ }
1754
+ }
1755
+ },
1756
+ "responses": {
1757
+ "200": {
1758
+ "description": "Resort rechazado",
1759
+ "content": {
1760
+ "application/json": {
1761
+ "schema": {
1762
+ "type": "object",
1763
+ "properties": {
1764
+ "id": {
1765
+ "type": "string",
1766
+ "format": "uuid"
1767
+ },
1768
+ "name": {
1769
+ "type": "string"
1770
+ },
1771
+ "status": {
1772
+ "type": "string",
1773
+ "enum": [
1774
+ "rejected"
1775
+ ]
1776
+ },
1777
+ "rejection_reason": {
1778
+ "type": "string"
1779
+ },
1780
+ "updated_at": {
1781
+ "type": "string",
1782
+ "format": "date-time"
1783
+ }
1784
+ }
1785
+ }
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+ }
1791
+ },
1792
+ "/api/v1/experiences/{experienceId}/availability": {
1793
+ "get": {
1794
+ "tags": [
1795
+ "Availability"
1796
+ ],
1797
+ "summary": "Consultar disponibilidad de experiencia",
1798
+ "description": "Retorna slots de disponibilidad agrupados por fecha con información de capacidad y precio",
1799
+ "parameters": [
1800
+ {
1801
+ "name": "experienceId",
1802
+ "in": "path",
1803
+ "required": true,
1804
+ "schema": {
1805
+ "type": "string",
1806
+ "format": "uuid"
1807
+ }
1808
+ },
1809
+ {
1810
+ "name": "from",
1811
+ "in": "query",
1812
+ "schema": {
1813
+ "type": "string",
1814
+ "format": "date",
1815
+ "description": "YYYY-MM-DD"
1816
+ }
1817
+ },
1818
+ {
1819
+ "name": "to",
1820
+ "in": "query",
1821
+ "schema": {
1822
+ "type": "string",
1823
+ "format": "date",
1824
+ "description": "YYYY-MM-DD"
1825
+ }
1826
+ },
1827
+ {
1828
+ "name": "limit",
1829
+ "in": "query",
1830
+ "schema": {
1831
+ "type": "integer",
1832
+ "default": 30,
1833
+ "minimum": 1,
1834
+ "maximum": 100
1835
+ }
1836
+ },
1837
+ {
1838
+ "name": "offset",
1839
+ "in": "query",
1840
+ "schema": {
1841
+ "type": "integer",
1842
+ "default": 0
1843
+ }
1844
+ },
1845
+ {
1846
+ "name": "include_full_slots",
1847
+ "in": "query",
1848
+ "schema": {
1849
+ "type": "boolean",
1850
+ "default": false
1851
+ }
1852
+ }
1853
+ ],
1854
+ "responses": {
1855
+ "200": {
1856
+ "description": "Disponibilidad encontrada",
1857
+ "content": {
1858
+ "application/json": {
1859
+ "schema": {
1860
+ "type": "object",
1861
+ "properties": {
1862
+ "experience_id": {
1863
+ "type": "string",
1864
+ "format": "uuid"
1865
+ },
1866
+ "availability": {
1867
+ "type": "array"
1868
+ },
1869
+ "query_params": {
1870
+ "type": "object"
1871
+ },
1872
+ "total_days": {
1873
+ "type": "integer"
1874
+ }
1875
+ }
1876
+ }
1877
+ }
1878
+ }
1879
+ }
1880
+ }
1881
+ }
1882
+ },
1883
+ "/api/v1/experiences/{experienceId}/availability/slot": {
1884
+ "post": {
1885
+ "tags": [
1886
+ "Availability"
1887
+ ],
1888
+ "summary": "Crear slot individual de disponibilidad",
1889
+ "parameters": [
1890
+ {
1891
+ "name": "experienceId",
1892
+ "in": "path",
1893
+ "required": true,
1894
+ "schema": {
1895
+ "type": "string",
1896
+ "format": "uuid"
1897
+ }
1898
+ }
1899
+ ],
1900
+ "requestBody": {
1901
+ "required": true,
1902
+ "content": {
1903
+ "application/json": {
1904
+ "schema": {
1905
+ "type": "object",
1906
+ "required": [
1907
+ "start_time",
1908
+ "end_time",
1909
+ "capacity"
1910
+ ],
1911
+ "properties": {
1912
+ "start_time": {
1913
+ "type": "string",
1914
+ "format": "date-time",
1915
+ "description": "ISO 8601"
1916
+ },
1917
+ "end_time": {
1918
+ "type": "string",
1919
+ "format": "date-time",
1920
+ "description": "ISO 8601"
1921
+ },
1922
+ "capacity": {
1923
+ "type": "integer",
1924
+ "minimum": 0
1925
+ }
1926
+ }
1927
+ }
1928
+ }
1929
+ }
1930
+ },
1931
+ "responses": {
1932
+ "201": {
1933
+ "description": "Slot creado",
1934
+ "content": {
1935
+ "application/json": {
1936
+ "schema": {
1937
+ "type": "object",
1938
+ "properties": {
1939
+ "message": {
1940
+ "type": "string"
1941
+ },
1942
+ "slot": {
1943
+ "type": "object"
1944
+ }
1945
+ }
1946
+ }
1947
+ }
1948
+ }
1949
+ }
1950
+ }
1951
+ }
1952
+ },
1953
+ "/api/v1/experiences/{experienceId}/availability/bulk": {
1954
+ "post": {
1955
+ "tags": [
1956
+ "Availability"
1957
+ ],
1958
+ "summary": "Crear múltiples slots (bulk)",
1959
+ "description": "Crea múltiples slots de disponibilidad para un rango de fechas con configuración de horarios",
1960
+ "parameters": [
1961
+ {
1962
+ "name": "experienceId",
1963
+ "in": "path",
1964
+ "required": true,
1965
+ "schema": {
1966
+ "type": "string",
1967
+ "format": "uuid"
1968
+ }
1969
+ }
1970
+ ],
1971
+ "requestBody": {
1972
+ "required": true,
1973
+ "content": {
1974
+ "application/json": {
1975
+ "schema": {
1976
+ "type": "object",
1977
+ "required": [
1978
+ "start_date",
1979
+ "end_date",
1980
+ "slots"
1981
+ ],
1982
+ "properties": {
1983
+ "start_date": {
1984
+ "type": "string",
1985
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
1986
+ "description": "YYYY-MM-DD"
1987
+ },
1988
+ "end_date": {
1989
+ "type": "string",
1990
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
1991
+ "description": "YYYY-MM-DD"
1992
+ },
1993
+ "capacity": {
1994
+ "type": "integer",
1995
+ "minimum": 0,
1996
+ "default": 10
1997
+ },
1998
+ "slots": {
1999
+ "type": "array",
2000
+ "items": {
2001
+ "type": "object",
2002
+ "required": [
2003
+ "start_hour",
2004
+ "start_minute",
2005
+ "end_hour",
2006
+ "end_minute"
2007
+ ],
2008
+ "properties": {
2009
+ "start_hour": {
2010
+ "type": "integer",
2011
+ "minimum": 0,
2012
+ "maximum": 23
2013
+ },
2014
+ "start_minute": {
2015
+ "type": "integer",
2016
+ "minimum": 0,
2017
+ "maximum": 59
2018
+ },
2019
+ "end_hour": {
2020
+ "type": "integer",
2021
+ "minimum": 0,
2022
+ "maximum": 23
2023
+ },
2024
+ "end_minute": {
2025
+ "type": "integer",
2026
+ "minimum": 0,
2027
+ "maximum": 59
2028
+ },
2029
+ "capacity": {
2030
+ "type": "integer",
2031
+ "minimum": 0
2032
+ },
2033
+ "days_of_week": {
2034
+ "type": "array",
2035
+ "items": {
2036
+ "type": "integer",
2037
+ "minimum": 0,
2038
+ "maximum": 6
2039
+ },
2040
+ "description": "0=Domingo, 1=Lunes, ..., 6=Sábado"
2041
+ }
2042
+ }
2043
+ }
2044
+ }
2045
+ }
2046
+ }
2047
+ }
2048
+ }
2049
+ },
2050
+ "responses": {
2051
+ "201": {
2052
+ "description": "Slots creados en bulk",
2053
+ "content": {
2054
+ "application/json": {
2055
+ "schema": {
2056
+ "type": "object",
2057
+ "properties": {
2058
+ "message": {
2059
+ "type": "string"
2060
+ },
2061
+ "results": {
2062
+ "type": "object",
2063
+ "properties": {
2064
+ "created_slots": {
2065
+ "type": "integer"
2066
+ },
2067
+ "skipped_slots": {
2068
+ "type": "integer"
2069
+ },
2070
+ "errors": {
2071
+ "type": "array",
2072
+ "items": {
2073
+ "type": "string"
2074
+ }
2075
+ }
2076
+ }
2077
+ },
2078
+ "experience_id": {
2079
+ "type": "string",
2080
+ "format": "uuid"
2081
+ },
2082
+ "date_range": {
2083
+ "type": "object",
2084
+ "properties": {
2085
+ "start_date": {
2086
+ "type": "string"
2087
+ },
2088
+ "end_date": {
2089
+ "type": "string"
2090
+ }
2091
+ }
2092
+ }
2093
+ }
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+ }
2099
+ }
2100
+ },
2101
+ "/api/v1/experiences/{experienceId}/availability/slots/{slotId}": {
2102
+ "delete": {
2103
+ "tags": [
2104
+ "Availability"
2105
+ ],
2106
+ "summary": "Eliminar slot de disponibilidad",
2107
+ "parameters": [
2108
+ {
2109
+ "name": "experienceId",
2110
+ "in": "path",
2111
+ "required": true,
2112
+ "schema": {
2113
+ "type": "string",
2114
+ "format": "uuid"
2115
+ }
2116
+ },
2117
+ {
2118
+ "name": "slotId",
2119
+ "in": "path",
2120
+ "required": true,
2121
+ "schema": {
2122
+ "type": "string",
2123
+ "format": "uuid"
2124
+ }
2125
+ }
2126
+ ],
2127
+ "responses": {
2128
+ "204": {
2129
+ "description": "Slot eliminado"
2130
+ }
2131
+ }
2132
+ }
2133
+ },
2134
+ "/api/v1/experiences/{experienceId}/availability/slots/{slotId}/remaining": {
2135
+ "get": {
2136
+ "tags": [
2137
+ "Availability"
2138
+ ],
2139
+ "summary": "Obtener capacidad restante de un slot",
2140
+ "parameters": [
2141
+ {
2142
+ "name": "experienceId",
2143
+ "in": "path",
2144
+ "required": true,
2145
+ "schema": {
2146
+ "type": "string",
2147
+ "format": "uuid"
2148
+ }
2149
+ },
2150
+ {
2151
+ "name": "slotId",
2152
+ "in": "path",
2153
+ "required": true,
2154
+ "schema": {
2155
+ "type": "string",
2156
+ "format": "uuid"
2157
+ }
2158
+ }
2159
+ ],
2160
+ "responses": {
2161
+ "200": {
2162
+ "description": "Capacidad restante",
2163
+ "content": {
2164
+ "application/json": {
2165
+ "schema": {
2166
+ "type": "object",
2167
+ "properties": {
2168
+ "experience_id": {
2169
+ "type": "string",
2170
+ "format": "uuid"
2171
+ },
2172
+ "slot_id": {
2173
+ "type": "string",
2174
+ "format": "uuid"
2175
+ },
2176
+ "remaining": {
2177
+ "type": "integer"
2178
+ }
2179
+ }
2180
+ }
2181
+ }
2182
+ }
2183
+ }
2184
+ }
2185
+ }
2186
+ },
2187
+ "/api/v1/experiences/{experienceId}/availability/debug": {
2188
+ "get": {
2189
+ "tags": [
2190
+ "Availability"
2191
+ ],
2192
+ "summary": "DEBUG: Ver todos los slots de una experiencia",
2193
+ "description": "Endpoint temporal para debugging - muestra todos los slots",
2194
+ "parameters": [
2195
+ {
2196
+ "name": "experienceId",
2197
+ "in": "path",
2198
+ "required": true,
2199
+ "schema": {
2200
+ "type": "string",
2201
+ "format": "uuid"
2202
+ }
2203
+ }
2204
+ ],
2205
+ "responses": {
2206
+ "200": {
2207
+ "description": "Todos los slots",
2208
+ "content": {
2209
+ "application/json": {
2210
+ "schema": {
2211
+ "type": "object",
2212
+ "properties": {
2213
+ "experience_id": {
2214
+ "type": "string",
2215
+ "format": "uuid"
2216
+ },
2217
+ "slots": {
2218
+ "type": "array"
2219
+ }
2220
+ }
2221
+ }
2222
+ }
2223
+ }
2224
+ }
2225
+ }
2226
+ }
2227
+ },
2228
+ "/api/v1/bookings": {
2229
+ "post": {
2230
+ "tags": [
2231
+ "Bookings"
2232
+ ],
2233
+ "summary": "Crear reserva pendiente (con lock de inventario)",
2234
+ "parameters": [
2235
+ {
2236
+ "name": "Idempotency-Key",
2237
+ "in": "header",
2238
+ "schema": {
2239
+ "type": "string",
2240
+ "maxLength": 128
2241
+ }
2242
+ }
2243
+ ],
2244
+ "requestBody": {
2245
+ "required": true,
2246
+ "content": {
2247
+ "application/json": {
2248
+ "schema": {
2249
+ "type": "object",
2250
+ "required": [
2251
+ "slotId",
2252
+ "experienceId",
2253
+ "adults",
2254
+ "subtotalCents",
2255
+ "taxCents"
2256
+ ],
2257
+ "properties": {
2258
+ "slotId": {
2259
+ "type": "string",
2260
+ "format": "uuid"
2261
+ },
2262
+ "experienceId": {
2263
+ "type": "string",
2264
+ "format": "uuid"
2265
+ },
2266
+ "adults": {
2267
+ "type": "integer",
2268
+ "minimum": 1
2269
+ },
2270
+ "children": {
2271
+ "type": "integer",
2272
+ "minimum": 0,
2273
+ "default": 0
2274
+ },
2275
+ "subtotalCents": {
2276
+ "type": "integer",
2277
+ "minimum": 0
2278
+ },
2279
+ "taxCents": {
2280
+ "type": "integer",
2281
+ "minimum": 0
2282
+ },
2283
+ "currency": {
2284
+ "type": "string",
2285
+ "pattern": "^[A-Z]{3}$",
2286
+ "default": "COP"
2287
+ },
2288
+ "agentId": {
2289
+ "type": "string",
2290
+ "format": "uuid"
2291
+ },
2292
+ "referralCode": {
2293
+ "type": "string"
2294
+ }
2295
+ }
2296
+ }
2297
+ }
2298
+ }
2299
+ },
2300
+ "responses": {
2301
+ "201": {
2302
+ "description": "Booking creado en estado pending con lock de inventario",
2303
+ "content": {
2304
+ "application/json": {
2305
+ "schema": {
2306
+ "type": "object",
2307
+ "properties": {
2308
+ "bookingId": {
2309
+ "type": "string",
2310
+ "format": "uuid"
2311
+ },
2312
+ "lockId": {
2313
+ "type": "string",
2314
+ "format": "uuid"
2315
+ },
2316
+ "status": {
2317
+ "type": "string",
2318
+ "enum": [
2319
+ "pending"
2320
+ ]
2321
+ },
2322
+ "expiresAt": {
2323
+ "type": "string",
2324
+ "format": "date-time"
2325
+ }
2326
+ }
2327
+ }
2328
+ }
2329
+ }
2330
+ }
2331
+ }
2332
+ }
2333
+ },
2334
+ "/api/v1/bookings/{bookingId}/confirm": {
2335
+ "patch": {
2336
+ "tags": [
2337
+ "Bookings"
2338
+ ],
2339
+ "summary": "Confirmar booking pendiente",
2340
+ "parameters": [
2341
+ {
2342
+ "name": "bookingId",
2343
+ "in": "path",
2344
+ "required": true,
2345
+ "schema": {
2346
+ "type": "string",
2347
+ "format": "uuid"
2348
+ }
2349
+ }
2350
+ ],
2351
+ "responses": {
2352
+ "204": {
2353
+ "description": "Booking confirmado"
2354
+ }
2355
+ }
2356
+ }
2357
+ },
2358
+ "/api/v1/bookings/{bookingId}/cancel": {
2359
+ "patch": {
2360
+ "tags": [
2361
+ "Bookings"
2362
+ ],
2363
+ "summary": "Cancelar booking pendiente",
2364
+ "parameters": [
2365
+ {
2366
+ "name": "bookingId",
2367
+ "in": "path",
2368
+ "required": true,
2369
+ "schema": {
2370
+ "type": "string",
2371
+ "format": "uuid"
2372
+ }
2373
+ }
2374
+ ],
2375
+ "requestBody": {
2376
+ "content": {
2377
+ "application/json": {
2378
+ "schema": {
2379
+ "type": "object",
2380
+ "properties": {
2381
+ "reason": {
2382
+ "type": "string"
2383
+ }
2384
+ }
2385
+ }
2386
+ }
2387
+ }
2388
+ },
2389
+ "responses": {
2390
+ "204": {
2391
+ "description": "Booking cancelado y lock liberado"
2392
+ }
2393
+ }
2394
+ }
2395
+ },
2396
+ "/api/v1/bookings/{bookingId}/cancel-confirmed": {
2397
+ "post": {
2398
+ "tags": [
2399
+ "Bookings"
2400
+ ],
2401
+ "summary": "Cancelar booking confirmado (con reembolso automático)",
2402
+ "description": "Cancela la reserva confirmada, libera inventario y procesa reembolso automáticamente",
2403
+ "parameters": [
2404
+ {
2405
+ "name": "bookingId",
2406
+ "in": "path",
2407
+ "required": true,
2408
+ "schema": {
2409
+ "type": "string",
2410
+ "format": "uuid"
2411
+ }
2412
+ }
2413
+ ],
2414
+ "requestBody": {
2415
+ "content": {
2416
+ "application/json": {
2417
+ "schema": {
2418
+ "type": "object",
2419
+ "properties": {
2420
+ "reason": {
2421
+ "type": "string"
2422
+ }
2423
+ }
2424
+ }
2425
+ }
2426
+ }
2427
+ },
2428
+ "responses": {
2429
+ "200": {
2430
+ "description": "Booking cancelado y reembolso procesado",
2431
+ "content": {
2432
+ "application/json": {
2433
+ "schema": {
2434
+ "type": "object",
2435
+ "properties": {
2436
+ "refund": {
2437
+ "type": "object",
2438
+ "properties": {
2439
+ "id": {
2440
+ "type": "string",
2441
+ "format": "uuid"
2442
+ },
2443
+ "amount": {
2444
+ "type": "integer"
2445
+ },
2446
+ "status": {
2447
+ "type": "string"
2448
+ }
2449
+ }
2450
+ }
2451
+ }
2452
+ }
2453
+ }
2454
+ }
2455
+ }
2456
+ }
2457
+ }
2458
+ },
2459
+ "/v1/payments": {
2460
+ "post": {
2461
+ "tags": [
2462
+ "Payments"
2463
+ ],
2464
+ "summary": "Crear pago para un booking",
2465
+ "description": "NOTA: Esta ruta usa /v1/payments (sin api/ prefix)",
2466
+ "parameters": [
2467
+ {
2468
+ "name": "idempotency-key",
2469
+ "in": "header",
2470
+ "schema": {
2471
+ "type": "string"
2472
+ }
2473
+ }
2474
+ ],
2475
+ "requestBody": {
2476
+ "required": true,
2477
+ "content": {
2478
+ "application/json": {
2479
+ "schema": {
2480
+ "type": "object",
2481
+ "required": [
2482
+ "bookingId",
2483
+ "provider"
2484
+ ],
2485
+ "properties": {
2486
+ "bookingId": {
2487
+ "type": "string",
2488
+ "format": "uuid"
2489
+ },
2490
+ "provider": {
2491
+ "type": "string",
2492
+ "enum": [
2493
+ "wompi",
2494
+ "epayco",
2495
+ "stripe",
2496
+ "paypal"
2497
+ ]
2498
+ },
2499
+ "paymentMethod": {
2500
+ "type": "string"
2501
+ },
2502
+ "redirectUrl": {
2503
+ "type": "string",
2504
+ "format": "uri"
2505
+ },
2506
+ "customerEmail": {
2507
+ "type": "string",
2508
+ "format": "email"
2509
+ }
2510
+ }
2511
+ }
2512
+ }
2513
+ }
2514
+ },
2515
+ "responses": {
2516
+ "201": {
2517
+ "description": "Pago creado",
2518
+ "content": {
2519
+ "application/json": {
2520
+ "schema": {
2521
+ "type": "object",
2522
+ "properties": {
2523
+ "id": {
2524
+ "type": "string",
2525
+ "format": "uuid"
2526
+ },
2527
+ "bookingId": {
2528
+ "type": "string",
2529
+ "format": "uuid"
2530
+ },
2531
+ "provider": {
2532
+ "type": "string"
2533
+ },
2534
+ "amount": {
2535
+ "type": "integer"
2536
+ },
2537
+ "currency": {
2538
+ "type": "string"
2539
+ },
2540
+ "status": {
2541
+ "type": "string",
2542
+ "enum": [
2543
+ "pending",
2544
+ "authorized",
2545
+ "paid",
2546
+ "failed",
2547
+ "cancelled",
2548
+ "refunded"
2549
+ ]
2550
+ },
2551
+ "checkoutUrl": {
2552
+ "type": "string",
2553
+ "format": "uri"
2554
+ },
2555
+ "expiresAt": {
2556
+ "type": "string",
2557
+ "format": "date-time"
2558
+ },
2559
+ "createdAt": {
2560
+ "type": "string",
2561
+ "format": "date-time"
2562
+ }
2563
+ }
2564
+ }
2565
+ }
2566
+ }
2567
+ }
2568
+ }
2569
+ }
2570
+ },
2571
+ "/v1/payments/{id}": {
2572
+ "get": {
2573
+ "tags": [
2574
+ "Payments"
2575
+ ],
2576
+ "summary": "Obtener pago por ID",
2577
+ "description": "NOTA: Esta ruta usa /v1/payments (sin api/ prefix)",
2578
+ "parameters": [
2579
+ {
2580
+ "name": "id",
2581
+ "in": "path",
2582
+ "required": true,
2583
+ "schema": {
2584
+ "type": "string",
2585
+ "format": "uuid"
2586
+ }
2587
+ }
2588
+ ],
2589
+ "responses": {
2590
+ "200": {
2591
+ "description": "Detalle del pago",
2592
+ "content": {
2593
+ "application/json": {
2594
+ "schema": {
2595
+ "type": "object",
2596
+ "properties": {
2597
+ "id": {
2598
+ "type": "string",
2599
+ "format": "uuid"
2600
+ },
2601
+ "bookingId": {
2602
+ "type": "string",
2603
+ "format": "uuid"
2604
+ },
2605
+ "provider": {
2606
+ "type": "string"
2607
+ },
2608
+ "providerPaymentId": {
2609
+ "type": "string"
2610
+ },
2611
+ "amount": {
2612
+ "type": "integer"
2613
+ },
2614
+ "currency": {
2615
+ "type": "string"
2616
+ },
2617
+ "status": {
2618
+ "type": "string"
2619
+ },
2620
+ "paymentMethod": {
2621
+ "type": "string"
2622
+ },
2623
+ "checkoutUrl": {
2624
+ "type": "string"
2625
+ },
2626
+ "expiresAt": {
2627
+ "type": "string",
2628
+ "format": "date-time"
2629
+ },
2630
+ "authorizedAt": {
2631
+ "type": "string",
2632
+ "format": "date-time"
2633
+ },
2634
+ "paidAt": {
2635
+ "type": "string",
2636
+ "format": "date-time"
2637
+ },
2638
+ "failedAt": {
2639
+ "type": "string",
2640
+ "format": "date-time"
2641
+ },
2642
+ "failureReason": {
2643
+ "type": "string"
2644
+ },
2645
+ "createdAt": {
2646
+ "type": "string",
2647
+ "format": "date-time"
2648
+ },
2649
+ "updatedAt": {
2650
+ "type": "string",
2651
+ "format": "date-time"
2652
+ }
2653
+ }
2654
+ }
2655
+ }
2656
+ }
2657
+ }
2658
+ }
2659
+ }
2660
+ },
2661
+ "/v1/payments/booking/{bookingId}": {
2662
+ "get": {
2663
+ "tags": [
2664
+ "Payments"
2665
+ ],
2666
+ "summary": "Obtener pagos de un booking",
2667
+ "description": "NOTA: Esta ruta usa /v1/payments (sin api/ prefix)",
2668
+ "parameters": [
2669
+ {
2670
+ "name": "bookingId",
2671
+ "in": "path",
2672
+ "required": true,
2673
+ "schema": {
2674
+ "type": "string",
2675
+ "format": "uuid"
2676
+ }
2677
+ }
2678
+ ],
2679
+ "responses": {
2680
+ "200": {
2681
+ "description": "Lista de pagos del booking"
2682
+ }
2683
+ }
2684
+ }
2685
+ },
2686
+ "/v1/payments/refunds": {
2687
+ "post": {
2688
+ "tags": [
2689
+ "Payments"
2690
+ ],
2691
+ "summary": "Crear reembolso manual",
2692
+ "description": "NOTA: Esta ruta usa /v1/payments (sin api/ prefix)",
2693
+ "requestBody": {
2694
+ "required": true,
2695
+ "content": {
2696
+ "application/json": {
2697
+ "schema": {
2698
+ "type": "object",
2699
+ "required": [
2700
+ "paymentId",
2701
+ "amountCents"
2702
+ ],
2703
+ "properties": {
2704
+ "paymentId": {
2705
+ "type": "string",
2706
+ "format": "uuid"
2707
+ },
2708
+ "amountCents": {
2709
+ "type": "integer",
2710
+ "minimum": 1
2711
+ },
2712
+ "reason": {
2713
+ "type": "string"
2714
+ }
2715
+ }
2716
+ }
2717
+ }
2718
+ }
2719
+ },
2720
+ "responses": {
2721
+ "201": {
2722
+ "description": "Reembolso creado",
2723
+ "content": {
2724
+ "application/json": {
2725
+ "schema": {
2726
+ "type": "object",
2727
+ "properties": {
2728
+ "id": {
2729
+ "type": "string",
2730
+ "format": "uuid"
2731
+ },
2732
+ "paymentId": {
2733
+ "type": "string",
2734
+ "format": "uuid"
2735
+ },
2736
+ "amount": {
2737
+ "type": "integer"
2738
+ },
2739
+ "currency": {
2740
+ "type": "string"
2741
+ },
2742
+ "status": {
2743
+ "type": "string"
2744
+ },
2745
+ "reason": {
2746
+ "type": "string"
2747
+ },
2748
+ "requestedAt": {
2749
+ "type": "string",
2750
+ "format": "date-time"
2751
+ },
2752
+ "createdAt": {
2753
+ "type": "string",
2754
+ "format": "date-time"
2755
+ }
2756
+ }
2757
+ }
2758
+ }
2759
+ }
2760
+ }
2761
+ }
2762
+ }
2763
+ },
2764
+ "/v1/payments/webhooks/{provider}": {
2765
+ "post": {
2766
+ "tags": [
2767
+ "Payments"
2768
+ ],
2769
+ "summary": "Webhook de proveedor de pagos",
2770
+ "description": "NOTA: Esta ruta usa /v1/payments (sin api/ prefix). Recibe webhooks de PayPal, Wompi, Epayco y Stripe",
2771
+ "security": [],
2772
+ "parameters": [
2773
+ {
2774
+ "name": "provider",
2775
+ "in": "path",
2776
+ "required": true,
2777
+ "schema": {
2778
+ "type": "string",
2779
+ "enum": [
2780
+ "paypal",
2781
+ "wompi",
2782
+ "epayco",
2783
+ "stripe"
2784
+ ]
2785
+ }
2786
+ },
2787
+ {
2788
+ "name": "paypal-transmission-id",
2789
+ "in": "header",
2790
+ "schema": {
2791
+ "type": "string"
2792
+ },
2793
+ "description": "Requerido para PayPal"
2794
+ },
2795
+ {
2796
+ "name": "paypal-transmission-sig",
2797
+ "in": "header",
2798
+ "schema": {
2799
+ "type": "string"
2800
+ },
2801
+ "description": "Requerido para PayPal"
2802
+ },
2803
+ {
2804
+ "name": "x-signature",
2805
+ "in": "header",
2806
+ "schema": {
2807
+ "type": "string"
2808
+ },
2809
+ "description": "Requerido para Wompi/Epayco/Stripe"
2810
+ }
2811
+ ],
2812
+ "requestBody": {
2813
+ "required": true,
2814
+ "content": {
2815
+ "application/json": {
2816
+ "schema": {
2817
+ "type": "object"
2818
+ }
2819
+ }
2820
+ }
2821
+ },
2822
+ "responses": {
2823
+ "200": {
2824
+ "description": "Webhook procesado",
2825
+ "content": {
2826
+ "application/json": {
2827
+ "schema": {
2828
+ "type": "object",
2829
+ "properties": {
2830
+ "success": {
2831
+ "type": "boolean"
2832
+ },
2833
+ "error": {
2834
+ "type": "string"
2835
+ }
2836
+ }
2837
+ }
2838
+ }
2839
+ }
2840
+ }
2841
+ }
2842
+ }
2843
+ },
2844
+ "/agents/resorts/{resortId}": {
2845
+ "post": {
2846
+ "tags": [
2847
+ "Agents"
2848
+ ],
2849
+ "summary": "Crear acuerdo de agente con resort",
2850
+ "parameters": [
2851
+ {
2852
+ "name": "resortId",
2853
+ "in": "path",
2854
+ "required": true,
2855
+ "schema": {
2856
+ "type": "string",
2857
+ "format": "uuid"
2858
+ }
2859
+ }
2860
+ ],
2861
+ "requestBody": {
2862
+ "required": true,
2863
+ "content": {
2864
+ "application/json": {
2865
+ "schema": {
2866
+ "type": "object",
2867
+ "required": [
2868
+ "userId",
2869
+ "commissionBps"
2870
+ ],
2871
+ "properties": {
2872
+ "userId": {
2873
+ "type": "string",
2874
+ "format": "uuid"
2875
+ },
2876
+ "commissionBps": {
2877
+ "type": "integer",
2878
+ "minimum": 0,
2879
+ "maximum": 10000
2880
+ }
2881
+ }
2882
+ }
2883
+ }
2884
+ }
2885
+ },
2886
+ "responses": {
2887
+ "201": {
2888
+ "description": "Acuerdo creado"
2889
+ }
2890
+ }
2891
+ },
2892
+ "get": {
2893
+ "tags": [
2894
+ "Agents"
2895
+ ],
2896
+ "summary": "Obtener agentes de un resort",
2897
+ "parameters": [
2898
+ {
2899
+ "name": "resortId",
2900
+ "in": "path",
2901
+ "required": true,
2902
+ "schema": {
2903
+ "type": "string",
2904
+ "format": "uuid"
2905
+ }
2906
+ }
2907
+ ],
2908
+ "responses": {
2909
+ "200": {
2910
+ "description": "Lista de agentes"
2911
+ }
2912
+ }
2913
+ }
2914
+ },
2915
+ "/agents/resorts/{resortId}/users/{userId}": {
2916
+ "patch": {
2917
+ "tags": [
2918
+ "Agents"
2919
+ ],
2920
+ "summary": "Actualizar comisión de agente",
2921
+ "parameters": [
2922
+ {
2923
+ "name": "resortId",
2924
+ "in": "path",
2925
+ "required": true,
2926
+ "schema": {
2927
+ "type": "string",
2928
+ "format": "uuid"
2929
+ }
2930
+ },
2931
+ {
2932
+ "name": "userId",
2933
+ "in": "path",
2934
+ "required": true,
2935
+ "schema": {
2936
+ "type": "string",
2937
+ "format": "uuid"
2938
+ }
2939
+ }
2940
+ ],
2941
+ "requestBody": {
2942
+ "required": true,
2943
+ "content": {
2944
+ "application/json": {
2945
+ "schema": {
2946
+ "type": "object",
2947
+ "required": [
2948
+ "commissionBps"
2949
+ ],
2950
+ "properties": {
2951
+ "commissionBps": {
2952
+ "type": "integer",
2953
+ "minimum": 0,
2954
+ "maximum": 10000
2955
+ }
2956
+ }
2957
+ }
2958
+ }
2959
+ }
2960
+ },
2961
+ "responses": {
2962
+ "200": {
2963
+ "description": "Comisión actualizada"
2964
+ }
2965
+ }
2966
+ }
2967
+ },
2968
+ "/agents/commissions": {
2969
+ "get": {
2970
+ "tags": [
2971
+ "Agents"
2972
+ ],
2973
+ "summary": "Obtener mis comisiones",
2974
+ "responses": {
2975
+ "200": {
2976
+ "description": "Lista de comisiones"
2977
+ }
2978
+ }
2979
+ }
2980
+ },
2981
+ "/agents/stats": {
2982
+ "get": {
2983
+ "tags": [
2984
+ "Agents"
2985
+ ],
2986
+ "summary": "Obtener mis estadísticas",
2987
+ "responses": {
2988
+ "200": {
2989
+ "description": "Estadísticas"
2990
+ }
2991
+ }
2992
+ }
2993
+ },
2994
+ "/agents/profile": {
2995
+ "get": {
2996
+ "tags": [
2997
+ "Agents"
2998
+ ],
2999
+ "summary": "Obtener mi perfil de agente",
3000
+ "responses": {
3001
+ "200": {
3002
+ "description": "Perfil"
3003
+ }
3004
+ }
3005
+ },
3006
+ "post": {
3007
+ "tags": [
3008
+ "Agents"
3009
+ ],
3010
+ "summary": "Actualizar mi perfil de agente",
3011
+ "requestBody": {
3012
+ "content": {
3013
+ "application/json": {
3014
+ "schema": {
3015
+ "type": "object",
3016
+ "properties": {
3017
+ "bankName": {
3018
+ "type": "string"
3019
+ },
3020
+ "accountNumber": {
3021
+ "type": "string"
3022
+ },
3023
+ "accountType": {
3024
+ "type": "string"
3025
+ },
3026
+ "accountHolderName": {
3027
+ "type": "string"
3028
+ },
3029
+ "taxId": {
3030
+ "type": "string"
3031
+ }
3032
+ }
3033
+ }
3034
+ }
3035
+ }
3036
+ },
3037
+ "responses": {
3038
+ "200": {
3039
+ "description": "Perfil actualizado"
3040
+ }
3041
+ }
3042
+ }
3043
+ },
3044
+ "/agents/referral-codes": {
3045
+ "post": {
3046
+ "tags": [
3047
+ "Agents"
3048
+ ],
3049
+ "summary": "Crear código de referido",
3050
+ "requestBody": {
3051
+ "required": true,
3052
+ "content": {
3053
+ "application/json": {
3054
+ "schema": {
3055
+ "type": "object",
3056
+ "required": [
3057
+ "code"
3058
+ ],
3059
+ "properties": {
3060
+ "code": {
3061
+ "type": "string"
3062
+ },
3063
+ "codeType": {
3064
+ "type": "string",
3065
+ "enum": [
3066
+ "commission",
3067
+ "discount",
3068
+ "both"
3069
+ ]
3070
+ },
3071
+ "discountType": {
3072
+ "type": "string",
3073
+ "enum": [
3074
+ "percentage",
3075
+ "fixed",
3076
+ "none"
3077
+ ]
3078
+ },
3079
+ "discountValue": {
3080
+ "type": "integer",
3081
+ "minimum": 0
3082
+ },
3083
+ "commissionOverrideBps": {
3084
+ "type": "integer",
3085
+ "minimum": 0,
3086
+ "maximum": 10000
3087
+ },
3088
+ "usageLimit": {
3089
+ "type": "integer",
3090
+ "minimum": 1
3091
+ },
3092
+ "expiresAt": {
3093
+ "type": "string",
3094
+ "format": "date-time"
3095
+ },
3096
+ "description": {
3097
+ "type": "string"
3098
+ },
3099
+ "allowStacking": {
3100
+ "type": "boolean"
3101
+ },
3102
+ "minPurchaseCents": {
3103
+ "type": "integer",
3104
+ "minimum": 0
3105
+ },
3106
+ "maxDiscountCents": {
3107
+ "type": "integer",
3108
+ "minimum": 0
3109
+ }
3110
+ }
3111
+ }
3112
+ }
3113
+ }
3114
+ },
3115
+ "responses": {
3116
+ "201": {
3117
+ "description": "Código creado"
3118
+ }
3119
+ }
3120
+ },
3121
+ "get": {
3122
+ "tags": [
3123
+ "Agents"
3124
+ ],
3125
+ "summary": "Obtener mis códigos de referido",
3126
+ "responses": {
3127
+ "200": {
3128
+ "description": "Lista de códigos"
3129
+ }
3130
+ }
3131
+ }
3132
+ },
3133
+ "/agents/referral-codes/{codeId}/toggle": {
3134
+ "post": {
3135
+ "tags": [
3136
+ "Agents"
3137
+ ],
3138
+ "summary": "Activar/desactivar código de referido",
3139
+ "parameters": [
3140
+ {
3141
+ "name": "codeId",
3142
+ "in": "path",
3143
+ "required": true,
3144
+ "schema": {
3145
+ "type": "string",
3146
+ "format": "uuid"
3147
+ }
3148
+ }
3149
+ ],
3150
+ "requestBody": {
3151
+ "required": true,
3152
+ "content": {
3153
+ "application/json": {
3154
+ "schema": {
3155
+ "type": "object",
3156
+ "required": [
3157
+ "isActive"
3158
+ ],
3159
+ "properties": {
3160
+ "isActive": {
3161
+ "type": "boolean"
3162
+ }
3163
+ }
3164
+ }
3165
+ }
3166
+ }
3167
+ },
3168
+ "responses": {
3169
+ "200": {
3170
+ "description": "Estado actualizado"
3171
+ }
3172
+ }
3173
+ }
3174
+ },
3175
+ "/agents/referral-codes/{codeId}/restrictions": {
3176
+ "post": {
3177
+ "tags": [
3178
+ "Agents"
3179
+ ],
3180
+ "summary": "Agregar restricción a código",
3181
+ "parameters": [
3182
+ {
3183
+ "name": "codeId",
3184
+ "in": "path",
3185
+ "required": true,
3186
+ "schema": {
3187
+ "type": "string",
3188
+ "format": "uuid"
3189
+ }
3190
+ }
3191
+ ],
3192
+ "requestBody": {
3193
+ "required": true,
3194
+ "content": {
3195
+ "application/json": {
3196
+ "schema": {
3197
+ "type": "object",
3198
+ "required": [
3199
+ "restrictionType"
3200
+ ],
3201
+ "properties": {
3202
+ "restrictionType": {
3203
+ "type": "string",
3204
+ "enum": [
3205
+ "experience",
3206
+ "category",
3207
+ "resort"
3208
+ ]
3209
+ },
3210
+ "experienceId": {
3211
+ "type": "string",
3212
+ "format": "uuid"
3213
+ },
3214
+ "categorySlug": {
3215
+ "type": "string"
3216
+ },
3217
+ "resortId": {
3218
+ "type": "string",
3219
+ "format": "uuid"
3220
+ }
3221
+ }
3222
+ }
3223
+ }
3224
+ }
3225
+ },
3226
+ "responses": {
3227
+ "201": {
3228
+ "description": "Restricción agregada"
3229
+ }
3230
+ }
3231
+ },
3232
+ "get": {
3233
+ "tags": [
3234
+ "Agents"
3235
+ ],
3236
+ "summary": "Obtener restricciones de código",
3237
+ "parameters": [
3238
+ {
3239
+ "name": "codeId",
3240
+ "in": "path",
3241
+ "required": true,
3242
+ "schema": {
3243
+ "type": "string",
3244
+ "format": "uuid"
3245
+ }
3246
+ }
3247
+ ],
3248
+ "responses": {
3249
+ "200": {
3250
+ "description": "Restricciones"
3251
+ }
3252
+ }
3253
+ }
3254
+ },
3255
+ "/agents/restrictions/{restrictionId}": {
3256
+ "delete": {
3257
+ "tags": [
3258
+ "Agents"
3259
+ ],
3260
+ "summary": "Eliminar restricción",
3261
+ "parameters": [
3262
+ {
3263
+ "name": "restrictionId",
3264
+ "in": "path",
3265
+ "required": true,
3266
+ "schema": {
3267
+ "type": "string",
3268
+ "format": "uuid"
3269
+ }
3270
+ }
3271
+ ],
3272
+ "responses": {
3273
+ "204": {
3274
+ "description": "Restricción eliminada"
3275
+ }
3276
+ }
3277
+ }
3278
+ },
3279
+ "/agents/referral-codes/{codeId}/variants": {
3280
+ "post": {
3281
+ "tags": [
3282
+ "Agents"
3283
+ ],
3284
+ "summary": "Crear variante de código (A/B testing)",
3285
+ "parameters": [
3286
+ {
3287
+ "name": "codeId",
3288
+ "in": "path",
3289
+ "required": true,
3290
+ "schema": {
3291
+ "type": "string",
3292
+ "format": "uuid"
3293
+ }
3294
+ }
3295
+ ],
3296
+ "requestBody": {
3297
+ "required": true,
3298
+ "content": {
3299
+ "application/json": {
3300
+ "schema": {
3301
+ "type": "object",
3302
+ "required": [
3303
+ "variantName",
3304
+ "code"
3305
+ ],
3306
+ "properties": {
3307
+ "variantName": {
3308
+ "type": "string"
3309
+ },
3310
+ "code": {
3311
+ "type": "string"
3312
+ },
3313
+ "discountValue": {
3314
+ "type": "integer",
3315
+ "minimum": 0
3316
+ },
3317
+ "commissionOverrideBps": {
3318
+ "type": "integer",
3319
+ "minimum": 0,
3320
+ "maximum": 10000
3321
+ }
3322
+ }
3323
+ }
3324
+ }
3325
+ }
3326
+ },
3327
+ "responses": {
3328
+ "201": {
3329
+ "description": "Variante creada"
3330
+ }
3331
+ }
3332
+ },
3333
+ "get": {
3334
+ "tags": [
3335
+ "Agents"
3336
+ ],
3337
+ "summary": "Obtener variantes de código",
3338
+ "parameters": [
3339
+ {
3340
+ "name": "codeId",
3341
+ "in": "path",
3342
+ "required": true,
3343
+ "schema": {
3344
+ "type": "string",
3345
+ "format": "uuid"
3346
+ }
3347
+ }
3348
+ ],
3349
+ "responses": {
3350
+ "200": {
3351
+ "description": "Variantes"
3352
+ }
3353
+ }
3354
+ }
3355
+ },
3356
+ "/agents/variants/{variantId}/toggle": {
3357
+ "post": {
3358
+ "tags": [
3359
+ "Agents"
3360
+ ],
3361
+ "summary": "Activar/desactivar variante",
3362
+ "parameters": [
3363
+ {
3364
+ "name": "variantId",
3365
+ "in": "path",
3366
+ "required": true,
3367
+ "schema": {
3368
+ "type": "string",
3369
+ "format": "uuid"
3370
+ }
3371
+ }
3372
+ ],
3373
+ "requestBody": {
3374
+ "required": true,
3375
+ "content": {
3376
+ "application/json": {
3377
+ "schema": {
3378
+ "type": "object",
3379
+ "required": [
3380
+ "isActive"
3381
+ ],
3382
+ "properties": {
3383
+ "isActive": {
3384
+ "type": "boolean"
3385
+ }
3386
+ }
3387
+ }
3388
+ }
3389
+ }
3390
+ },
3391
+ "responses": {
3392
+ "200": {
3393
+ "description": "Estado actualizado"
3394
+ }
3395
+ }
3396
+ }
3397
+ },
3398
+ "/agents/analytics": {
3399
+ "get": {
3400
+ "tags": [
3401
+ "Agents"
3402
+ ],
3403
+ "summary": "Obtener analytics de mis códigos",
3404
+ "parameters": [
3405
+ {
3406
+ "name": "codeId",
3407
+ "in": "query",
3408
+ "schema": {
3409
+ "type": "string",
3410
+ "format": "uuid"
3411
+ }
3412
+ }
3413
+ ],
3414
+ "responses": {
3415
+ "200": {
3416
+ "description": "Analytics"
3417
+ }
3418
+ }
3419
+ }
3420
+ },
3421
+ "/agents/referral-codes/{codeId}/variant-analytics": {
3422
+ "get": {
3423
+ "tags": [
3424
+ "Agents"
3425
+ ],
3426
+ "summary": "Obtener analytics de variantes",
3427
+ "parameters": [
3428
+ {
3429
+ "name": "codeId",
3430
+ "in": "path",
3431
+ "required": true,
3432
+ "schema": {
3433
+ "type": "string",
3434
+ "format": "uuid"
3435
+ }
3436
+ }
3437
+ ],
3438
+ "responses": {
3439
+ "200": {
3440
+ "description": "Analytics de variantes"
3441
+ }
3442
+ }
3443
+ }
3444
+ },
3445
+ "/api/v1/admin/dashboard": {
3446
+ "get": {
3447
+ "tags": [
3448
+ "Admin"
3449
+ ],
3450
+ "summary": "Obtener métricas del dashboard",
3451
+ "responses": {
3452
+ "200": {
3453
+ "description": "Métricas del dashboard",
3454
+ "content": {
3455
+ "application/json": {
3456
+ "schema": {
3457
+ "type": "object",
3458
+ "properties": {
3459
+ "resorts": {
3460
+ "type": "object",
3461
+ "properties": {
3462
+ "draft": {
3463
+ "type": "integer"
3464
+ },
3465
+ "under_review": {
3466
+ "type": "integer"
3467
+ },
3468
+ "approved": {
3469
+ "type": "integer"
3470
+ },
3471
+ "rejected": {
3472
+ "type": "integer"
3473
+ },
3474
+ "total": {
3475
+ "type": "integer"
3476
+ }
3477
+ }
3478
+ },
3479
+ "experiences": {
3480
+ "type": "object",
3481
+ "properties": {
3482
+ "draft": {
3483
+ "type": "integer"
3484
+ },
3485
+ "under_review": {
3486
+ "type": "integer"
3487
+ },
3488
+ "active": {
3489
+ "type": "integer"
3490
+ },
3491
+ "rejected": {
3492
+ "type": "integer"
3493
+ },
3494
+ "total": {
3495
+ "type": "integer"
3496
+ }
3497
+ }
3498
+ },
3499
+ "recentActivity": {
3500
+ "type": "array",
3501
+ "items": {
3502
+ "type": "object",
3503
+ "properties": {
3504
+ "action": {
3505
+ "type": "string"
3506
+ },
3507
+ "entity_type": {
3508
+ "type": "string"
3509
+ },
3510
+ "entity_id": {
3511
+ "type": "string",
3512
+ "format": "uuid"
3513
+ },
3514
+ "after": {
3515
+ "type": "object"
3516
+ },
3517
+ "created_at": {
3518
+ "type": "string",
3519
+ "format": "date-time"
3520
+ },
3521
+ "admin_email": {
3522
+ "type": "string",
3523
+ "format": "email"
3524
+ }
3525
+ }
3526
+ }
3527
+ },
3528
+ "timestamp": {
3529
+ "type": "string",
3530
+ "format": "date-time"
3531
+ }
3532
+ }
3533
+ }
3534
+ }
3535
+ }
3536
+ },
3537
+ "401": {
3538
+ "description": "No autorizado"
3539
+ },
3540
+ "403": {
3541
+ "description": "Acceso denegado - Se requiere rol de administrador"
3542
+ }
3543
+ }
3544
+ }
3545
+ },
3546
+ "/api/v1/admin/resorts/review": {
3547
+ "get": {
3548
+ "tags": [
3549
+ "Admin"
3550
+ ],
3551
+ "summary": "Obtener resorts pendientes de revisión",
3552
+ "parameters": [
3553
+ {
3554
+ "name": "page",
3555
+ "in": "query",
3556
+ "required": false,
3557
+ "schema": {
3558
+ "type": "integer",
3559
+ "minimum": 1,
3560
+ "default": 1
3561
+ }
3562
+ },
3563
+ {
3564
+ "name": "limit",
3565
+ "in": "query",
3566
+ "required": false,
3567
+ "schema": {
3568
+ "type": "integer",
3569
+ "minimum": 1,
3570
+ "maximum": 100,
3571
+ "default": 10
3572
+ }
3573
+ },
3574
+ {
3575
+ "name": "search",
3576
+ "in": "query",
3577
+ "required": false,
3578
+ "schema": {
3579
+ "type": "string"
3580
+ }
3581
+ }
3582
+ ],
3583
+ "responses": {
3584
+ "200": {
3585
+ "description": "Resorts para revisar",
3586
+ "content": {
3587
+ "application/json": {
3588
+ "schema": {
3589
+ "type": "object",
3590
+ "properties": {
3591
+ "data": {
3592
+ "type": "array",
3593
+ "items": {
3594
+ "type": "object",
3595
+ "properties": {
3596
+ "id": {
3597
+ "type": "string",
3598
+ "format": "uuid"
3599
+ },
3600
+ "name": {
3601
+ "type": "string"
3602
+ },
3603
+ "description": {
3604
+ "type": ["string", "null"]
3605
+ },
3606
+ "contact_email": {
3607
+ "type": ["string", "null"],
3608
+ "format": "email"
3609
+ },
3610
+ "contact_phone": {
3611
+ "type": ["string", "null"]
3612
+ },
3613
+ "address_line": {
3614
+ "type": ["string", "null"]
3615
+ },
3616
+ "city": {
3617
+ "type": ["string", "null"]
3618
+ },
3619
+ "country": {
3620
+ "type": ["string", "null"]
3621
+ },
3622
+ "latitude": {
3623
+ "type": ["number", "null"]
3624
+ },
3625
+ "longitude": {
3626
+ "type": ["number", "null"]
3627
+ },
3628
+ "owner_user_id": {
3629
+ "type": ["string", "null"],
3630
+ "format": "uuid"
3631
+ },
3632
+ "is_active": {
3633
+ "type": "boolean"
3634
+ },
3635
+ "status": {
3636
+ "type": "string",
3637
+ "enum": [
3638
+ "draft",
3639
+ "under_review",
3640
+ "approved",
3641
+ "rejected"
3642
+ ]
3643
+ },
3644
+ "approved_by": {
3645
+ "type": ["string", "null"],
3646
+ "format": "uuid"
3647
+ },
3648
+ "approved_at": {
3649
+ "type": ["string", "null"],
3650
+ "format": "date-time"
3651
+ },
3652
+ "rejection_reason": {
3653
+ "type": ["string", "null"]
3654
+ },
3655
+ "created_at": {
3656
+ "type": "string",
3657
+ "format": "date-time"
3658
+ },
3659
+ "updated_at": {
3660
+ "type": "string",
3661
+ "format": "date-time"
3662
+ },
3663
+ "owner_email": {
3664
+ "type": ["string", "null"],
3665
+ "format": "email"
3666
+ },
3667
+ "owner_name": {
3668
+ "type": ["string", "null"]
3669
+ }
3670
+ }
3671
+ }
3672
+ },
3673
+ "meta": {
3674
+ "type": "object",
3675
+ "properties": {
3676
+ "total": {
3677
+ "type": "integer"
3678
+ },
3679
+ "page": {
3680
+ "type": "integer"
3681
+ },
3682
+ "limit": {
3683
+ "type": "integer"
3684
+ },
3685
+ "totalPages": {
3686
+ "type": "integer"
3687
+ },
3688
+ "hasNextPage": {
3689
+ "type": "boolean"
3690
+ },
3691
+ "hasPreviousPage": {
3692
+ "type": "boolean"
3693
+ }
3694
+ }
3695
+ }
3696
+ }
3697
+ }
3698
+ }
3699
+ }
3700
+ },
3701
+ "401": {
3702
+ "description": "No autorizado"
3703
+ },
3704
+ "403": {
3705
+ "description": "Acceso denegado - Se requiere rol de administrador"
3706
+ }
3707
+ }
3708
+ }
3709
+ },
3710
+ "/api/v1/admin/resorts/{id}/approve": {
3711
+ "post": {
3712
+ "tags": [
3713
+ "Admin"
3714
+ ],
3715
+ "summary": "Aprobar resort",
3716
+ "parameters": [
3717
+ {
3718
+ "name": "id",
3719
+ "in": "path",
3720
+ "required": true,
3721
+ "schema": {
3722
+ "type": "string",
3723
+ "format": "uuid"
3724
+ }
3725
+ }
3726
+ ],
3727
+ "requestBody": {
3728
+ "content": {
3729
+ "application/json": {
3730
+ "schema": {
3731
+ "type": "object",
3732
+ "properties": {
3733
+ "notes": {
3734
+ "type": "string",
3735
+ "maxLength": 500
3736
+ }
3737
+ }
3738
+ }
3739
+ }
3740
+ }
3741
+ },
3742
+ "responses": {
3743
+ "200": {
3744
+ "description": "Resort aprobado",
3745
+ "content": {
3746
+ "application/json": {
3747
+ "schema": {
3748
+ "type": "object",
3749
+ "properties": {
3750
+ "id": {
3751
+ "type": "string",
3752
+ "format": "uuid"
3753
+ },
3754
+ "name": {
3755
+ "type": "string"
3756
+ },
3757
+ "description": {
3758
+ "type": ["string", "null"]
3759
+ },
3760
+ "contact_email": {
3761
+ "type": ["string", "null"],
3762
+ "format": "email"
3763
+ },
3764
+ "contact_phone": {
3765
+ "type": ["string", "null"]
3766
+ },
3767
+ "address_line": {
3768
+ "type": ["string", "null"]
3769
+ },
3770
+ "city": {
3771
+ "type": ["string", "null"]
3772
+ },
3773
+ "country": {
3774
+ "type": ["string", "null"]
3775
+ },
3776
+ "latitude": {
3777
+ "type": ["number", "null"]
3778
+ },
3779
+ "longitude": {
3780
+ "type": ["number", "null"]
3781
+ },
3782
+ "owner_user_id": {
3783
+ "type": ["string", "null"],
3784
+ "format": "uuid"
3785
+ },
3786
+ "is_active": {
3787
+ "type": "boolean"
3788
+ },
3789
+ "status": {
3790
+ "type": "string",
3791
+ "enum": [
3792
+ "approved"
3793
+ ]
3794
+ },
3795
+ "approved_by": {
3796
+ "type": "string",
3797
+ "format": "uuid"
3798
+ },
3799
+ "approved_at": {
3800
+ "type": "string",
3801
+ "format": "date-time"
3802
+ },
3803
+ "rejection_reason": {
3804
+ "type": ["string", "null"]
3805
+ },
3806
+ "created_at": {
3807
+ "type": "string",
3808
+ "format": "date-time"
3809
+ },
3810
+ "updated_at": {
3811
+ "type": "string",
3812
+ "format": "date-time"
3813
+ }
3814
+ }
3815
+ }
3816
+ }
3817
+ }
3818
+ },
3819
+ "400": {
3820
+ "description": "Solo resorts en revisión pueden ser aprobados"
3821
+ },
3822
+ "401": {
3823
+ "description": "No autorizado"
3824
+ },
3825
+ "403": {
3826
+ "description": "Acceso denegado - Se requiere rol de administrador"
3827
+ },
3828
+ "404": {
3829
+ "description": "Resort no encontrado"
3830
+ }
3831
+ }
3832
+ }
3833
+ },
3834
+ "/api/v1/admin/resorts/{id}/reject": {
3835
+ "post": {
3836
+ "tags": [
3837
+ "Admin"
3838
+ ],
3839
+ "summary": "Rechazar resort",
3840
+ "parameters": [
3841
+ {
3842
+ "name": "id",
3843
+ "in": "path",
3844
+ "required": true,
3845
+ "schema": {
3846
+ "type": "string",
3847
+ "format": "uuid"
3848
+ }
3849
+ }
3850
+ ],
3851
+ "requestBody": {
3852
+ "required": true,
3853
+ "content": {
3854
+ "application/json": {
3855
+ "schema": {
3856
+ "type": "object",
3857
+ "required": [
3858
+ "rejection_reason"
3859
+ ],
3860
+ "properties": {
3861
+ "rejection_reason": {
3862
+ "type": "string",
3863
+ "maxLength": 500
3864
+ }
3865
+ }
3866
+ }
3867
+ }
3868
+ }
3869
+ },
3870
+ "responses": {
3871
+ "200": {
3872
+ "description": "Resort rechazado",
3873
+ "content": {
3874
+ "application/json": {
3875
+ "schema": {
3876
+ "type": "object",
3877
+ "properties": {
3878
+ "id": {
3879
+ "type": "string",
3880
+ "format": "uuid"
3881
+ },
3882
+ "name": {
3883
+ "type": "string"
3884
+ },
3885
+ "description": {
3886
+ "type": ["string", "null"]
3887
+ },
3888
+ "contact_email": {
3889
+ "type": ["string", "null"],
3890
+ "format": "email"
3891
+ },
3892
+ "contact_phone": {
3893
+ "type": ["string", "null"]
3894
+ },
3895
+ "address_line": {
3896
+ "type": ["string", "null"]
3897
+ },
3898
+ "city": {
3899
+ "type": ["string", "null"]
3900
+ },
3901
+ "country": {
3902
+ "type": ["string", "null"]
3903
+ },
3904
+ "latitude": {
3905
+ "type": ["number", "null"]
3906
+ },
3907
+ "longitude": {
3908
+ "type": ["number", "null"]
3909
+ },
3910
+ "owner_user_id": {
3911
+ "type": ["string", "null"],
3912
+ "format": "uuid"
3913
+ },
3914
+ "is_active": {
3915
+ "type": "boolean"
3916
+ },
3917
+ "status": {
3918
+ "type": "string",
3919
+ "enum": [
3920
+ "rejected"
3921
+ ]
3922
+ },
3923
+ "approved_by": {
3924
+ "type": "string",
3925
+ "format": "uuid"
3926
+ },
3927
+ "approved_at": {
3928
+ "type": "string",
3929
+ "format": "date-time"
3930
+ },
3931
+ "rejection_reason": {
3932
+ "type": "string"
3933
+ },
3934
+ "created_at": {
3935
+ "type": "string",
3936
+ "format": "date-time"
3937
+ },
3938
+ "updated_at": {
3939
+ "type": "string",
3940
+ "format": "date-time"
3941
+ }
3942
+ }
3943
+ }
3944
+ }
3945
+ }
3946
+ },
3947
+ "400": {
3948
+ "description": "Solo resorts en revisión pueden ser rechazados"
3949
+ },
3950
+ "401": {
3951
+ "description": "No autorizado"
3952
+ },
3953
+ "403": {
3954
+ "description": "Acceso denegado - Se requiere rol de administrador"
3955
+ },
3956
+ "404": {
3957
+ "description": "Resort no encontrado"
3958
+ }
3959
+ }
3960
+ }
3961
+ },
3962
+ "/api/v1/admin/experiences/review": {
3963
+ "get": {
3964
+ "tags": [
3965
+ "Admin"
3966
+ ],
3967
+ "summary": "Obtener experiencias pendientes de revisión",
3968
+ "parameters": [
3969
+ {
3970
+ "name": "page",
3971
+ "in": "query",
3972
+ "required": false,
3973
+ "schema": {
3974
+ "type": "integer",
3975
+ "minimum": 1,
3976
+ "default": 1
3977
+ }
3978
+ },
3979
+ {
3980
+ "name": "limit",
3981
+ "in": "query",
3982
+ "required": false,
3983
+ "schema": {
3984
+ "type": "integer",
3985
+ "minimum": 1,
3986
+ "maximum": 100,
3987
+ "default": 10
3988
+ }
3989
+ },
3990
+ {
3991
+ "name": "search",
3992
+ "in": "query",
3993
+ "required": false,
3994
+ "schema": {
3995
+ "type": "string"
3996
+ }
3997
+ }
3998
+ ],
3999
+ "responses": {
4000
+ "200": {
4001
+ "description": "Experiencias para revisar",
4002
+ "content": {
4003
+ "application/json": {
4004
+ "schema": {
4005
+ "type": "object",
4006
+ "properties": {
4007
+ "data": {
4008
+ "type": "array",
4009
+ "items": {
4010
+ "type": "object",
4011
+ "properties": {
4012
+ "id": {
4013
+ "type": "string",
4014
+ "format": "uuid"
4015
+ },
4016
+ "resort_id": {
4017
+ "type": "string",
4018
+ "format": "uuid"
4019
+ },
4020
+ "title": {
4021
+ "type": "string"
4022
+ },
4023
+ "slug": {
4024
+ "type": "string"
4025
+ },
4026
+ "description": {
4027
+ "type": ["string", "null"]
4028
+ },
4029
+ "category": {
4030
+ "type": "string",
4031
+ "enum": [
4032
+ "islands",
4033
+ "nautical",
4034
+ "city_tour"
4035
+ ]
4036
+ },
4037
+ "price_cents": {
4038
+ "type": "integer"
4039
+ },
4040
+ "currency": {
4041
+ "type": "string"
4042
+ },
4043
+ "includes": {
4044
+ "type": ["string", "null"]
4045
+ },
4046
+ "excludes": {
4047
+ "type": ["string", "null"]
4048
+ },
4049
+ "main_image_url": {
4050
+ "type": ["string", "null"]
4051
+ },
4052
+ "status": {
4053
+ "type": "string",
4054
+ "enum": [
4055
+ "draft",
4056
+ "under_review",
4057
+ "active",
4058
+ "rejected"
4059
+ ]
4060
+ },
4061
+ "rating_avg": {
4062
+ "type": "number"
4063
+ },
4064
+ "rating_count": {
4065
+ "type": "integer"
4066
+ },
4067
+ "approved_by": {
4068
+ "type": ["string", "null"],
4069
+ "format": "uuid"
4070
+ },
4071
+ "approved_at": {
4072
+ "type": ["string", "null"],
4073
+ "format": "date-time"
4074
+ },
4075
+ "rejection_reason": {
4076
+ "type": ["string", "null"]
4077
+ },
4078
+ "created_at": {
4079
+ "type": "string",
4080
+ "format": "date-time"
4081
+ },
4082
+ "updated_at": {
4083
+ "type": "string",
4084
+ "format": "date-time"
4085
+ },
4086
+ "resort_name": {
4087
+ "type": ["string", "null"]
4088
+ },
4089
+ "resort_city": {
4090
+ "type": ["string", "null"]
4091
+ }
4092
+ }
4093
+ }
4094
+ },
4095
+ "meta": {
4096
+ "type": "object",
4097
+ "properties": {
4098
+ "total": {
4099
+ "type": "integer"
4100
+ },
4101
+ "page": {
4102
+ "type": "integer"
4103
+ },
4104
+ "limit": {
4105
+ "type": "integer"
4106
+ },
4107
+ "totalPages": {
4108
+ "type": "integer"
4109
+ },
4110
+ "hasNextPage": {
4111
+ "type": "boolean"
4112
+ },
4113
+ "hasPreviousPage": {
4114
+ "type": "boolean"
4115
+ }
4116
+ }
4117
+ }
4118
+ }
4119
+ }
4120
+ }
4121
+ }
4122
+ },
4123
+ "401": {
4124
+ "description": "No autorizado"
4125
+ },
4126
+ "403": {
4127
+ "description": "Acceso denegado - Se requiere rol de administrador"
4128
+ }
4129
+ }
4130
+ }
4131
+ },
4132
+ "/api/v1/admin/experiences/{id}/approve": {
4133
+ "post": {
4134
+ "tags": [
4135
+ "Admin"
4136
+ ],
4137
+ "summary": "Aprobar experiencia",
4138
+ "parameters": [
4139
+ {
4140
+ "name": "id",
4141
+ "in": "path",
4142
+ "required": true,
4143
+ "schema": {
4144
+ "type": "string",
4145
+ "format": "uuid"
4146
+ }
4147
+ }
4148
+ ],
4149
+ "requestBody": {
4150
+ "content": {
4151
+ "application/json": {
4152
+ "schema": {
4153
+ "type": "object",
4154
+ "properties": {
4155
+ "notes": {
4156
+ "type": "string",
4157
+ "maxLength": 500
4158
+ }
4159
+ }
4160
+ }
4161
+ }
4162
+ }
4163
+ },
4164
+ "responses": {
4165
+ "200": {
4166
+ "description": "Experiencia aprobada",
4167
+ "content": {
4168
+ "application/json": {
4169
+ "schema": {
4170
+ "type": "object",
4171
+ "properties": {
4172
+ "id": {
4173
+ "type": "string",
4174
+ "format": "uuid"
4175
+ },
4176
+ "resort_id": {
4177
+ "type": "string",
4178
+ "format": "uuid"
4179
+ },
4180
+ "title": {
4181
+ "type": "string"
4182
+ },
4183
+ "slug": {
4184
+ "type": "string"
4185
+ },
4186
+ "description": {
4187
+ "type": ["string", "null"]
4188
+ },
4189
+ "category": {
4190
+ "type": "string",
4191
+ "enum": [
4192
+ "islands",
4193
+ "nautical",
4194
+ "city_tour"
4195
+ ]
4196
+ },
4197
+ "price_cents": {
4198
+ "type": "integer"
4199
+ },
4200
+ "currency": {
4201
+ "type": "string"
4202
+ },
4203
+ "includes": {
4204
+ "type": ["string", "null"]
4205
+ },
4206
+ "excludes": {
4207
+ "type": ["string", "null"]
4208
+ },
4209
+ "main_image_url": {
4210
+ "type": ["string", "null"]
4211
+ },
4212
+ "status": {
4213
+ "type": "string",
4214
+ "enum": [
4215
+ "active"
4216
+ ]
4217
+ },
4218
+ "rating_avg": {
4219
+ "type": "number"
4220
+ },
4221
+ "rating_count": {
4222
+ "type": "integer"
4223
+ },
4224
+ "approved_by": {
4225
+ "type": "string",
4226
+ "format": "uuid"
4227
+ },
4228
+ "approved_at": {
4229
+ "type": "string",
4230
+ "format": "date-time"
4231
+ },
4232
+ "rejection_reason": {
4233
+ "type": ["string", "null"]
4234
+ },
4235
+ "created_at": {
4236
+ "type": "string",
4237
+ "format": "date-time"
4238
+ },
4239
+ "updated_at": {
4240
+ "type": "string",
4241
+ "format": "date-time"
4242
+ }
4243
+ }
4244
+ }
4245
+ }
4246
+ }
4247
+ },
4248
+ "400": {
4249
+ "description": "Solo experiencias en revisión pueden ser aprobadas"
4250
+ },
4251
+ "401": {
4252
+ "description": "No autorizado"
4253
+ },
4254
+ "403": {
4255
+ "description": "Acceso denegado - Se requiere rol de administrador"
4256
+ },
4257
+ "404": {
4258
+ "description": "Experiencia no encontrada"
4259
+ }
4260
+ }
4261
+ }
4262
+ },
4263
+ "/api/v1/admin/experiences/{id}/reject": {
4264
+ "post": {
4265
+ "tags": [
4266
+ "Admin"
4267
+ ],
4268
+ "summary": "Rechazar experiencia",
4269
+ "parameters": [
4270
+ {
4271
+ "name": "id",
4272
+ "in": "path",
4273
+ "required": true,
4274
+ "schema": {
4275
+ "type": "string",
4276
+ "format": "uuid"
4277
+ }
4278
+ }
4279
+ ],
4280
+ "requestBody": {
4281
+ "required": true,
4282
+ "content": {
4283
+ "application/json": {
4284
+ "schema": {
4285
+ "type": "object",
4286
+ "required": [
4287
+ "rejection_reason"
4288
+ ],
4289
+ "properties": {
4290
+ "rejection_reason": {
4291
+ "type": "string",
4292
+ "maxLength": 500
4293
+ }
4294
+ }
4295
+ }
4296
+ }
4297
+ }
4298
+ },
4299
+ "responses": {
4300
+ "200": {
4301
+ "description": "Experiencia rechazada",
4302
+ "content": {
4303
+ "application/json": {
4304
+ "schema": {
4305
+ "type": "object",
4306
+ "properties": {
4307
+ "id": {
4308
+ "type": "string",
4309
+ "format": "uuid"
4310
+ },
4311
+ "resort_id": {
4312
+ "type": "string",
4313
+ "format": "uuid"
4314
+ },
4315
+ "title": {
4316
+ "type": "string"
4317
+ },
4318
+ "slug": {
4319
+ "type": "string"
4320
+ },
4321
+ "description": {
4322
+ "type": ["string", "null"]
4323
+ },
4324
+ "category": {
4325
+ "type": "string",
4326
+ "enum": [
4327
+ "islands",
4328
+ "nautical",
4329
+ "city_tour"
4330
+ ]
4331
+ },
4332
+ "price_cents": {
4333
+ "type": "integer"
4334
+ },
4335
+ "currency": {
4336
+ "type": "string"
4337
+ },
4338
+ "includes": {
4339
+ "type": ["string", "null"]
4340
+ },
4341
+ "excludes": {
4342
+ "type": ["string", "null"]
4343
+ },
4344
+ "main_image_url": {
4345
+ "type": ["string", "null"]
4346
+ },
4347
+ "status": {
4348
+ "type": "string",
4349
+ "enum": [
4350
+ "rejected"
4351
+ ]
4352
+ },
4353
+ "rating_avg": {
4354
+ "type": "number"
4355
+ },
4356
+ "rating_count": {
4357
+ "type": "integer"
4358
+ },
4359
+ "approved_by": {
4360
+ "type": "string",
4361
+ "format": "uuid"
4362
+ },
4363
+ "approved_at": {
4364
+ "type": "string",
4365
+ "format": "date-time"
4366
+ },
4367
+ "rejection_reason": {
4368
+ "type": "string"
4369
+ },
4370
+ "created_at": {
4371
+ "type": "string",
4372
+ "format": "date-time"
4373
+ },
4374
+ "updated_at": {
4375
+ "type": "string",
4376
+ "format": "date-time"
4377
+ }
4378
+ }
4379
+ }
4380
+ }
4381
+ }
4382
+ },
4383
+ "400": {
4384
+ "description": "Solo experiencias en revisión pueden ser rechazadas"
4385
+ },
4386
+ "401": {
4387
+ "description": "No autorizado"
4388
+ },
4389
+ "403": {
4390
+ "description": "Acceso denegado - Se requiere rol de administrador"
4391
+ },
4392
+ "404": {
4393
+ "description": "Experiencia no encontrada"
4394
+ }
4395
+ }
4396
+ }
4397
+ },
4398
+ "/api/v1/admin/audit-logs": {
4399
+ "get": {
4400
+ "tags": [
4401
+ "Admin"
4402
+ ],
4403
+ "summary": "Obtener logs de auditoría",
4404
+ "parameters": [
4405
+ {
4406
+ "name": "page",
4407
+ "in": "query",
4408
+ "required": false,
4409
+ "schema": {
4410
+ "type": "integer",
4411
+ "minimum": 1,
4412
+ "default": 1
4413
+ }
4414
+ },
4415
+ {
4416
+ "name": "limit",
4417
+ "in": "query",
4418
+ "required": false,
4419
+ "schema": {
4420
+ "type": "integer",
4421
+ "minimum": 1,
4422
+ "maximum": 100,
4423
+ "default": 20
4424
+ }
4425
+ },
4426
+ {
4427
+ "name": "search",
4428
+ "in": "query",
4429
+ "required": false,
4430
+ "schema": {
4431
+ "type": "string"
4432
+ }
4433
+ }
4434
+ ],
4435
+ "responses": {
4436
+ "200": {
4437
+ "description": "Logs de auditoría",
4438
+ "content": {
4439
+ "application/json": {
4440
+ "schema": {
4441
+ "type": "object",
4442
+ "properties": {
4443
+ "data": {
4444
+ "type": "array",
4445
+ "items": {
4446
+ "type": "object",
4447
+ "properties": {
4448
+ "id": {
4449
+ "type": "string",
4450
+ "format": "uuid"
4451
+ },
4452
+ "actor_user_id": {
4453
+ "type": "string",
4454
+ "format": "uuid"
4455
+ },
4456
+ "actor_role": {
4457
+ "type": "string"
4458
+ },
4459
+ "action": {
4460
+ "type": "string"
4461
+ },
4462
+ "entity_type": {
4463
+ "type": "string"
4464
+ },
4465
+ "entity_id": {
4466
+ "type": "string",
4467
+ "format": "uuid"
4468
+ },
4469
+ "before": {
4470
+ "type": "object"
4471
+ },
4472
+ "after": {
4473
+ "type": "object"
4474
+ },
4475
+ "created_at": {
4476
+ "type": "string",
4477
+ "format": "date-time"
4478
+ },
4479
+ "actor_email": {
4480
+ "type": ["string", "null"],
4481
+ "format": "email"
4482
+ },
4483
+ "actor_name": {
4484
+ "type": ["string", "null"]
4485
+ }
4486
+ }
4487
+ }
4488
+ },
4489
+ "meta": {
4490
+ "type": "object",
4491
+ "properties": {
4492
+ "total": {
4493
+ "type": "integer"
4494
+ },
4495
+ "page": {
4496
+ "type": "integer"
4497
+ },
4498
+ "limit": {
4499
+ "type": "integer"
4500
+ },
4501
+ "totalPages": {
4502
+ "type": "integer"
4503
+ },
4504
+ "hasNextPage": {
4505
+ "type": "boolean"
4506
+ },
4507
+ "hasPreviousPage": {
4508
+ "type": "boolean"
4509
+ }
4510
+ }
4511
+ }
4512
+ }
4513
+ }
4514
+ }
4515
+ }
4516
+ },
4517
+ "401": {
4518
+ "description": "No autorizado"
4519
+ },
4520
+ "403": {
4521
+ "description": "Acceso denegado - Se requiere rol de administrador"
4522
+ }
4523
+ }
4524
+ }
4525
+ }
4526
+ },
4527
+ "components": {
4528
+ "securitySchemes": {
4529
+ "BearerAuth": {
4530
+ "type": "http",
4531
+ "scheme": "bearer",
4532
+ "bearerFormat": "JWT"
4533
+ }
301
4534
  }
302
4535
  }
303
-
4536
+ }