{
  "components": {
    "responses": {},
    "schemas": {
      "ApproveValuesInput": {
        "properties": {
          "parameter": {
            "enum": [
              "utm_source",
              "utm_medium",
              "utm_campaign",
              "utm_term",
              "utm_content"
            ],
            "type": "string"
          },
          "values": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "parameter",
          "values"
        ],
        "title": "ApproveValuesInput",
        "type": "object"
      },
      "ApprovedValue": {
        "properties": {
          "id": {
            "type": "integer"
          },
          "parameter": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "title": "ApprovedValue",
        "type": "object"
      },
      "ApprovedValueList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/ApprovedValue"
            },
            "type": "array"
          }
        },
        "title": "ApprovedValueList",
        "type": "object"
      },
      "Campaign": {
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "nullable": true,
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "link_count": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "title": "Campaign",
        "type": "object"
      },
      "CampaignInput": {
        "properties": {
          "description": {
            "nullable": true,
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "CampaignInput",
        "type": "object"
      },
      "CampaignList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Campaign"
            },
            "type": "array"
          }
        },
        "title": "CampaignList",
        "type": "object"
      },
      "CampaignUpdateInput": {
        "properties": {
          "description": {
            "nullable": true,
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "title": "CampaignUpdateInput",
        "type": "object"
      },
      "Convention": {
        "properties": {
          "casing": {
            "type": "string"
          },
          "mode": {
            "enum": [
              "block",
              "warn"
            ],
            "type": "string"
          },
          "required_parameters": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "separator": {
            "type": "string"
          }
        },
        "title": "Convention",
        "type": "object"
      },
      "ConventionInput": {
        "description": "Partial by design: unnamed rules are left alone.",
        "properties": {
          "casing": {
            "enum": [
              "lower",
              "upper",
              "any"
            ],
            "type": "string"
          },
          "mode": {
            "enum": [
              "block",
              "warn"
            ],
            "type": "string"
          },
          "required_parameters": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "separator": {
            "enum": [
              "hyphen",
              "underscore",
              "any"
            ],
            "type": "string"
          }
        },
        "title": "ConventionInput",
        "type": "object"
      },
      "Conversion": {
        "description": "A recorded conversion. `attributed_by` says how the link was found on a 201; `duplicate: true` marks the 200 a retry gets.",
        "properties": {
          "attributed_by": {
            "enum": [
              "reference",
              "binding"
            ],
            "nullable": true,
            "type": "string"
          },
          "currency": {
            "nullable": true,
            "type": "string"
          },
          "customer_id": {
            "type": "string"
          },
          "duplicate": {
            "nullable": true,
            "type": "boolean"
          },
          "event_id": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "kind": {
            "type": "string"
          },
          "link": {
            "nullable": true,
            "type": "string"
          },
          "occurred_at": {
            "format": "date-time",
            "type": "string"
          },
          "value": {
            "nullable": true,
            "type": "string"
          }
        },
        "title": "Conversion",
        "type": "object"
      },
      "ConversionInput": {
        "description": "One result the customer's server reports (spec 046). `link` is the `_link` value the landing URL carried and is required on a customer's first conversion; afterwards the binding answers.",
        "properties": {
          "currency": {
            "description": "Three letters, shape only; kept as given, never converted",
            "nullable": true,
            "type": "string"
          },
          "customer_id": {
            "description": "The customer's identifier in the caller's own system",
            "maxLength": 128,
            "minLength": 1,
            "type": "string"
          },
          "event_id": {
            "description": "The caller's identifier for this event; a repeat is a retry",
            "maxLength": 128,
            "minLength": 1,
            "type": "string"
          },
          "kind": {
            "description": "purchase, signup, lead, or a word of the customer's own",
            "maxLength": 32,
            "nullable": true,
            "type": "string"
          },
          "link": {
            "description": "`<host>/<short_code>`, exactly the `_link` value the landing URL carried",
            "nullable": true,
            "type": "string"
          },
          "occurred_at": {
            "description": "When it happened. Defaults to now; refused far in the future or past",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "value": {
            "description": "Decimal string, >= 0. Requires currency",
            "nullable": true,
            "pattern": "^\\d+(\\.\\d+)?$",
            "type": "string"
          }
        },
        "required": [
          "customer_id",
          "event_id"
        ],
        "title": "ConversionInput",
        "type": "object"
      },
      "DependencyRule": {
        "properties": {
          "driving_parameter": {
            "type": "string"
          },
          "driving_value": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "target": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        },
        "title": "DependencyRule",
        "type": "object"
      },
      "DependencyRuleInput": {
        "properties": {
          "driving_parameter": {
            "type": "string"
          },
          "driving_value": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "values": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "driving_parameter",
          "driving_value",
          "target",
          "values"
        ],
        "title": "DependencyRuleInput",
        "type": "object"
      },
      "DependencyRuleList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/DependencyRule"
            },
            "type": "array"
          }
        },
        "title": "DependencyRuleList",
        "type": "object"
      },
      "Error": {
        "description": "Every refusal. `code` is the closed set of FR-019.",
        "properties": {
          "error": {
            "properties": {
              "code": {
                "enum": [
                  "unauthenticated",
                  "subscription_required",
                  "subscription_ended",
                  "missing_permission",
                  "role_forbids",
                  "plan_forbids",
                  "rate_limited",
                  "governance_refused",
                  "scanner_refused",
                  "limit_reached",
                  "duplicate_link",
                  "not_found",
                  "invalid_input"
                ],
                "type": "string"
              },
              "details": {
                "additionalProperties": true,
                "description": "field → messages, on invalid_input",
                "nullable": true,
                "type": "object"
              },
              "message": {
                "type": "string"
              },
              "permission": {
                "nullable": true,
                "type": "string"
              },
              "retry_after_seconds": {
                "nullable": true,
                "type": "integer"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "title": "Error",
        "type": "object"
      },
      "Link": {
        "properties": {
          "android_url": {
            "nullable": true,
            "type": "string"
          },
          "campaign_id": {
            "nullable": true,
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "created_by": {
            "properties": {
              "api_token_id": {
                "nullable": true,
                "type": "integer"
              },
              "user_id": {
                "type": "integer"
              }
            },
            "type": "object"
          },
          "destination": {
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "ios_url": {
            "nullable": true,
            "type": "string"
          },
          "note": {
            "nullable": true,
            "type": "string"
          },
          "qr_url": {
            "description": "the short URL with the scan marker",
            "format": "uri",
            "type": "string"
          },
          "short_code": {
            "type": "string"
          },
          "short_url": {
            "format": "uri",
            "type": "string"
          },
          "state": {
            "enum": [
              "live",
              "pending",
              "blocked",
              "expired"
            ],
            "type": "string"
          },
          "tags": {
            "description": "the link's tags, by name without regard to case",
            "items": {
              "properties": {
                "color": {
                  "enum": [
                    "gray",
                    "red",
                    "orange",
                    "amber",
                    "green",
                    "teal",
                    "blue",
                    "purple"
                  ],
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "utm_campaign": {
            "nullable": true,
            "type": "string"
          },
          "utm_content": {
            "nullable": true,
            "type": "string"
          },
          "utm_medium": {
            "nullable": true,
            "type": "string"
          },
          "utm_source": {
            "nullable": true,
            "type": "string"
          },
          "utm_term": {
            "nullable": true,
            "type": "string"
          }
        },
        "title": "Link",
        "type": "object"
      },
      "LinkInput": {
        "properties": {
          "android_url": {
            "description": "optional Android destination (spec 047): an app link or a Play page, https only; phones on Android are routed here, blank clears it",
            "nullable": true,
            "type": "string"
          },
          "campaign_id": {
            "nullable": true,
            "type": "integer"
          },
          "destination": {
            "format": "uri",
            "nullable": true,
            "type": "string"
          },
          "domain": {
            "description": "optional; a verified custom domain name, else the shared domain",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "ios_url": {
            "description": "optional iOS destination (spec 047): an app link or an App Store page, https only; phones on iOS are routed here, blank clears it",
            "nullable": true,
            "type": "string"
          },
          "note": {
            "description": "free text kept on the link; blank clears it",
            "nullable": true,
            "type": "string"
          },
          "short_code": {
            "description": "optional; random when omitted",
            "type": "string"
          },
          "tags": {
            "description": "tag names (spec 039): an existing tag of the workspace by name, case-insensitive, else a new one; on update the list replaces the link's tags and [] clears them",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "utm_campaign": {
            "type": "string"
          },
          "utm_content": {
            "type": "string"
          },
          "utm_medium": {
            "type": "string"
          },
          "utm_source": {
            "type": "string"
          },
          "utm_term": {
            "type": "string"
          },
          "whatsapp_message": {
            "description": "the prefilled message, plain text, never URL-encoded",
            "nullable": true,
            "type": "string"
          },
          "whatsapp_number": {
            "description": "a WhatsApp click-to-chat link (issue #202): the phone in international format (+, spaces and dashes are stripped). Given INSTEAD of destination — the wa.me URL is composed and the message encoded once, server-side",
            "nullable": true,
            "type": "string"
          }
        },
        "title": "LinkInput",
        "type": "object"
      },
      "LinkList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Link"
            },
            "type": "array"
          },
          "next_after": {
            "description": "pass as `after` for the next page",
            "nullable": true,
            "type": "integer"
          }
        },
        "title": "LinkList",
        "type": "object"
      },
      "LinkUpdateInput": {
        "properties": {
          "android_url": {
            "description": "optional Android destination (spec 047): an app link or a Play page, https only; phones on Android are routed here, null or blank clears it",
            "nullable": true,
            "type": "string"
          },
          "campaign_id": {
            "nullable": true,
            "type": "integer"
          },
          "destination": {
            "format": "uri",
            "nullable": true,
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "ios_url": {
            "description": "optional iOS destination (spec 047): an app link or an App Store page, https only; phones on iOS are routed here, null or blank clears it",
            "nullable": true,
            "type": "string"
          },
          "note": {
            "description": "free text kept on the link; blank clears it",
            "nullable": true,
            "type": "string"
          },
          "tags": {
            "description": "tag names (spec 039): an existing tag of the workspace by name, case-insensitive, else a new one; the list replaces the link's tags and [] clears them",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "utm_campaign": {
            "type": "string"
          },
          "utm_content": {
            "type": "string"
          },
          "utm_medium": {
            "type": "string"
          },
          "utm_source": {
            "type": "string"
          },
          "utm_term": {
            "type": "string"
          },
          "whatsapp_message": {
            "description": "the prefilled message, plain text, never URL-encoded",
            "nullable": true,
            "type": "string"
          },
          "whatsapp_number": {
            "description": "a WhatsApp click-to-chat link (issue #202): the phone in international format (+, spaces and dashes are stripped). Given INSTEAD of destination — the wa.me URL is composed and the message encoded once, server-side",
            "nullable": true,
            "type": "string"
          }
        },
        "title": "LinkUpdateInput",
        "type": "object"
      },
      "LinkWithWarning": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Link"
          },
          {
            "properties": {
              "warning": {
                "description": "governance warning when the convention warns rather than blocks",
                "nullable": true,
                "type": "string"
              }
            },
            "type": "object"
          }
        ],
        "title": "LinkWithWarning"
      },
      "Report": {
        "properties": {
          "capped": {
            "type": "boolean"
          },
          "comparison": {
            "description": "Present only when compare=true: the previous period, or {unavailable: sentence}",
            "properties": {
              "change": {
                "properties": {
                  "clicks": {
                    "nullable": true,
                    "type": "integer"
                  },
                  "conversions": {
                    "nullable": true,
                    "type": "integer"
                  },
                  "scans": {
                    "nullable": true,
                    "type": "integer"
                  },
                  "uniques": {
                    "nullable": true,
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "from": {
                "format": "date",
                "type": "string"
              },
              "to": {
                "format": "date",
                "type": "string"
              },
              "totals": {
                "properties": {
                  "clicks": {
                    "type": "integer"
                  },
                  "conversions": {
                    "type": "integer"
                  },
                  "scans": {
                    "type": "integer"
                  },
                  "uniques": {
                    "type": "integer"
                  }
                },
                "type": "object"
              },
              "unavailable": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "from": {
            "format": "date",
            "type": "string"
          },
          "group_by": {
            "type": "string"
          },
          "rows": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array"
          },
          "to": {
            "format": "date",
            "type": "string"
          },
          "totals": {
            "properties": {
              "clicks": {
                "type": "integer"
              },
              "conversions": {
                "type": "integer"
              },
              "scans": {
                "type": "integer"
              },
              "uniques": {
                "type": "integer"
              },
              "value": {
                "additionalProperties": {
                  "type": "string"
                },
                "description": "currency → decimal string, summed per currency; null when no conversion in the period carried a value. Never summed across currencies (spec 046)",
                "nullable": true,
                "type": "object"
              }
            },
            "type": "object"
          }
        },
        "title": "Report",
        "type": "object"
      },
      "ReportLink": {
        "description": "A client report link (#418). Its URL is returned once, at creation, and is never stored.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "host": {
            "description": "The domain the report is served on",
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "revoked_at": {
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "state": {
            "enum": [
              "live",
              "revoked"
            ],
            "type": "string"
          }
        },
        "title": "ReportLink",
        "type": "object"
      },
      "ReportLinkCreated": {
        "description": "The new report link, with its URL — the only time the URL is returned. Anyone holding it can read the workspace's live report without signing in.",
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "host": {
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "state": {
            "enum": [
              "live"
            ],
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "title": "ReportLinkCreated",
        "type": "object"
      },
      "ReportLinkList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/ReportLink"
            },
            "type": "array"
          }
        },
        "title": "ReportLinkList",
        "type": "object"
      },
      "Tag": {
        "properties": {
          "color": {
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "type": "integer"
          },
          "link_count": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "title": "Tag",
        "type": "object"
      },
      "TagInput": {
        "properties": {
          "color": {
            "enum": [
              "gray",
              "red",
              "orange",
              "amber",
              "green",
              "teal",
              "blue",
              "purple"
            ],
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "TagInput",
        "type": "object"
      },
      "TagList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Tag"
            },
            "type": "array"
          }
        },
        "title": "TagList",
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearer": {
        "description": "utmk_<lookup><secret>, minted on /tokens",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Every request carries `Authorization: Bearer utmk_…`. The token fixes the workspace; no request names one. Every operation declares `x-permission`, the token permission it requires.",
    "title": "UTMKit API",
    "version": "1"
  },
  "openapi": "3.0.0",
  "paths": {
    "/api/v1/analytics/report": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `analytics:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.AnalyticsController.report",
        "parameters": [
          {
            "description": "",
            "in": "query",
            "name": "from",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "to",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "group_by",
            "required": false,
            "schema": {
              "enum": [
                "day",
                "country",
                "device",
                "referrer",
                "utm_source",
                "utm_medium",
                "utm_campaign",
                "utm_term",
                "utm_content",
                "trigger",
                "event_type",
                "destination"
              ],
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "short_code",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "host",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "campaign_id",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "tag",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "compare",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Report"
                }
              }
            },
            "description": "Report"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Click report — scans reported apart from clicks",
        "tags": [
          "analytics"
        ],
        "x-permission": "analytics:read"
      }
    },
    "/api/v1/campaigns": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `campaigns:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.CampaignController.index",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignList"
                }
              }
            },
            "description": "Campaigns"
          }
        },
        "summary": "List campaigns of the token's workspace, with their link counts",
        "tags": [
          "campaigns"
        ],
        "x-permission": "campaigns:read"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `campaigns:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.CampaignController.create",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignInput"
              }
            }
          },
          "description": "Campaign input",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            },
            "description": "Campaign"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Create a campaign — a repeated name returns the existing one",
        "tags": [
          "campaigns"
        ],
        "x-permission": "campaigns:write"
      }
    },
    "/api/v1/campaigns/{id}": {
      "delete": {
        "callbacks": {},
        "description": "Requires a token with the `campaigns:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.CampaignController.delete",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Delete a campaign — its links keep working and only lose the container",
        "tags": [
          "campaigns"
        ],
        "x-permission": "campaigns:write"
      },
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `campaigns:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.CampaignController.show",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            },
            "description": "Campaign"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "One campaign",
        "tags": [
          "campaigns"
        ],
        "x-permission": "campaigns:read"
      },
      "patch": {
        "callbacks": {},
        "description": "Requires a token with the `campaigns:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.CampaignController.update",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignUpdateInput"
              }
            }
          },
          "description": "Campaign update input",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            },
            "description": "Campaign"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Rename or re-describe — existing links keep their utm_campaign",
        "tags": [
          "campaigns"
        ],
        "x-permission": "campaigns:write"
      }
    },
    "/api/v1/conversions": {
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `conversions:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.ConversionController.create",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionInput"
              }
            }
          },
          "description": "Conversion input",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversion"
                }
              }
            },
            "description": "Conversion (a retry, already recorded)"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversion"
                }
              }
            },
            "description": "Conversion"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Report a conversion against one of the workspace's links",
        "tags": [
          "conversions"
        ],
        "x-permission": "conversions:write"
      }
    },
    "/api/v1/links": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `links:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.LinkController.index",
        "parameters": [
          {
            "description": "search string — free terms and key:value filters (spec 042); the keys are domain (the destination's host, not the short link's domain), code, tag, campaign (the container's name), state (live|pending|withdrawn), destination (unreachable|reachable), channel (misfiled|ok — where Google Analytics 4 will file the clicks against where the values were aiming), utm_source, utm_medium, utm_campaign, utm_term, utm_content; quote a value with spaces",
            "in": "query",
            "name": "q",
            "required": false,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          },
          {
            "description": "cursor — id of the last link seen",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 100,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkList"
                }
              }
            },
            "description": "Links"
          }
        },
        "summary": "List links of the token's workspace, newest first",
        "tags": [
          "links"
        ],
        "x-permission": "links:read"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `links:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.LinkController.create",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkInput"
              }
            }
          },
          "description": "Link input",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkWithWarning"
                }
              }
            },
            "description": "Link"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Create a governed short link",
        "tags": [
          "links"
        ],
        "x-permission": "links:write"
      }
    },
    "/api/v1/links/{id}": {
      "delete": {
        "callbacks": {},
        "description": "Requires a token with the `links:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.LinkController.delete",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Withdraw the link — removed from the edge on the console's own path",
        "tags": [
          "links"
        ],
        "x-permission": "links:write"
      },
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `links:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.LinkController.show",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            },
            "description": "Link"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "One link",
        "tags": [
          "links"
        ],
        "x-permission": "links:read"
      },
      "patch": {
        "callbacks": {},
        "description": "Requires a token with the `links:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.LinkController.update",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkUpdateInput"
              }
            }
          },
          "description": "Link update input",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkWithWarning"
                }
              }
            },
            "description": "Link"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Update destination, UTMs or expiry; re-validated and re-scanned",
        "tags": [
          "links"
        ],
        "x-permission": "links:write"
      }
    },
    "/api/v1/report-links": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `reports:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.ReportLinkController.index",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportLinkList"
                }
              }
            },
            "description": "Report links"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "List the workspace's client report links, live first — never their URLs",
        "tags": [
          "reports"
        ],
        "x-permission": "reports:write"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `reports:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.ReportLinkController.create",
        "parameters": [],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportLinkCreated"
                }
              }
            },
            "description": "Report link"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Publish a client report link — the URL is returned this once",
        "tags": [
          "reports"
        ],
        "x-permission": "reports:write"
      }
    },
    "/api/v1/report-links/{id}": {
      "delete": {
        "callbacks": {},
        "description": "Requires a token with the `reports:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.ReportLinkController.delete",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportLink"
                }
              }
            },
            "description": "Report link"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Revoke a client report link — whoever holds it sees nothing from the next request",
        "tags": [
          "reports"
        ],
        "x-permission": "reports:write"
      }
    },
    "/api/v1/rules/convention": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `rules:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.convention",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Convention"
                }
              }
            },
            "description": "Convention"
          }
        },
        "summary": "The workspace's UTM convention",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:read"
      },
      "put": {
        "callbacks": {},
        "description": "Requires a token with the `rules:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.put_convention",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConventionInput"
              }
            }
          },
          "description": "Convention",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Convention"
                }
              }
            },
            "description": "Convention"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Update the convention — casing, separator, required parameters, mode; a field left out keeps its value",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:write"
      }
    },
    "/api/v1/rules/dependencies": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `rules:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.dependencies",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DependencyRuleList"
                }
              }
            },
            "description": "Dependency rules"
          }
        },
        "summary": "The dependency rules, one row per allowed value under a driving pair",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:read"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `rules:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.add_dependency",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DependencyRuleInput"
              }
            }
          },
          "description": "Dependency rule",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DependencyRuleList"
                }
              }
            },
            "description": "Dependency rules"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Allow values for a target under a driving pair — additive",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:write"
      }
    },
    "/api/v1/rules/dependencies/{id}": {
      "delete": {
        "callbacks": {},
        "description": "Requires a token with the `rules:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.remove_dependency",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Remove one dependency rule — one allowed value, not the whole set",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:write"
      }
    },
    "/api/v1/rules/values": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `rules:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.values",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovedValueList"
                }
              }
            },
            "description": "Approved values"
          }
        },
        "summary": "The approved values, per UTM parameter",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:read"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `rules:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.approve_values",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApproveValuesInput"
              }
            }
          },
          "description": "Values to approve",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovedValueList"
                }
              }
            },
            "description": "Approved values"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Approve values for one parameter — additive, repeats are no-ops",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:write"
      }
    },
    "/api/v1/rules/values/{id}": {
      "delete": {
        "callbacks": {},
        "description": "Requires a token with the `rules:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.RulesController.retire_value",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Retired"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Retire an approved value — retiring the last one OPENS the parameter",
        "tags": [
          "rules"
        ],
        "x-permission": "rules:write"
      }
    },
    "/api/v1/tags": {
      "get": {
        "callbacks": {},
        "description": "Requires a token with the `links:read` permission.",
        "operationId": "UtmBuilderWeb.API.V1.TagController.index",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TagList"
                }
              }
            },
            "description": "Tags"
          }
        },
        "summary": "List the tags of the token's workspace, with their link counts",
        "tags": [
          "tags"
        ],
        "x-permission": "links:read"
      },
      "post": {
        "callbacks": {},
        "description": "Requires a token with the `links:write` permission.",
        "operationId": "UtmBuilderWeb.API.V1.TagController.create",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagInput"
              }
            }
          },
          "description": "Tag input",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tag"
                }
              }
            },
            "description": "Tag"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Error"
          }
        },
        "summary": "Create a tag ahead of any link — the colour may be chosen",
        "tags": [
          "tags"
        ],
        "x-permission": "links:write"
      }
    }
  },
  "security": [
    {
      "bearer": []
    }
  ],
  "servers": [
    {
      "url": "https://app.utmkit.co",
      "variables": {}
    }
  ],
  "tags": []
}
