{
  "openapi": "3.1.0",
  "info": {
    "title": "MyQRx API",
    "version": "1.0.0",
    "description": "Dynamic QR codes and short links on tilg.in that you can repoint after printing, with safety-checked destinations and scan analytics. Every plan includes the API, Free too (5 dynamic codes, unlimited static). Only dynamic codes use quota. Recommended loop: create one dynamic code per placement → read analytics → compare → PATCH the weaker code's destination → measure again. Docs: https://myqrx.com/qr-code-api",
    "contact": {
      "url": "https://myqrx.com/qr-code-api"
    }
  },
  "servers": [
    {
      "url": "https://myqrx.com/api/v1"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "paths": {
    "/codes": {
      "get": {
        "operationId": "listCodes",
        "summary": "List your codes, newest first, 50 a page.",
        "x-ability": "codes:read",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "dynamic",
                "static"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of codes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Code"
                      }
                    },
                    "links": {
                      "type": "object"
                    },
                    "meta": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createCode",
        "summary": "Create a code. With no `type` it is a dynamic short_url code, which uses one quota slot; static types are unlimited.",
        "description": "Choose static only for content that never changes and will never be measured (Wi-Fi, vCard). Send an Idempotency-Key header: a retry with the same key and body within 24 hours returns the first response and creates nothing. Every dynamic destination is safety-checked; scans see a safety notice until the verdict is clean.",
        "x-ability": "codes:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCode"
              },
              "examples": {
                "dynamic": {
                  "value": {
                    "url": "https://example.com/menu",
                    "name": "Andheri store window"
                  }
                },
                "wifi": {
                  "value": {
                    "type": "wifi",
                    "ssid": "Cafe",
                    "encryption": "WPA",
                    "password": "secret123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Code"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/codes/{code}": {
      "parameters": [
        {
          "name": "code",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The code's id, as returned by the API (the same id the dashboard uses in its URLs)."
        }
      ],
      "get": {
        "operationId": "getCode",
        "summary": "One code, with its destination and safety verdict.",
        "x-ability": "codes:read",
        "responses": {
          "200": {
            "description": "The code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Code"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "operationId": "updateCode",
        "summary": "Rename a code and/or change a dynamic code's destination. Printed copies follow the change.",
        "description": "This is the act step after a comparison. The new destination is safety-checked; scans see a safety notice until it clears. Refused (409 edit_refused) for a bio page's code, or while the account is over its plan allowance.",
        "x-ability": "codes:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 120
                  },
                  "destination": {
                    "type": "string",
                    "format": "uri"
                  }
                },
                "minProperties": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Code"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/codes/{code}/image": {
      "get": {
        "operationId": "getCodeImage",
        "summary": "The code as SVG, PNG, JPG or PDF, with its saved design.",
        "x-ability": "codes:read",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The code's id, as returned by the API (the same id the dashboard uses in its URLs)."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "svg",
                "png",
                "jpg",
                "pdf"
              ],
              "default": "svg"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1024
            },
            "description": "Pixels, for PNG/JPG. One of the download sizes the dashboard offers."
          }
        ],
        "responses": {
          "200": {
            "description": "The image.",
            "content": {
              "image/svg+xml": {},
              "image/png": {},
              "image/jpeg": {},
              "application/pdf": {}
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/codes/{code}/analytics": {
      "get": {
        "operationId": "getScanAnalytics",
        "summary": "A decision-ready scan summary for one dynamic code: is it working, where, on what, and better or worse than before?",
        "x-ability": "analytics:read",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The code's id, as returned by the API (the same id the dashboard uses in its URLs)."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "First day (YYYY-MM-DD, India time). Default: 29 days before `to`."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Last day (YYYY-MM-DD). Default: today. A range spans at most 731 days."
          }
        ],
        "responses": {
          "200": {
            "description": "The summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ScanSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/analytics/compare": {
      "get": {
        "operationId": "compareCodes",
        "summary": "Rank 2–10 of your dynamic codes by scans over the same period: which placement, store or print run did best?",
        "x-ability": "analytics:read",
        "parameters": [
          {
            "name": "codes[]",
            "in": "query",
            "required": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 2,
              "maxItems": 10
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "First day (YYYY-MM-DD, India time). Default: 29 days before `to`."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Last day (YYYY-MM-DD). Default: today. A range spans at most 731 days."
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked summaries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "period": {
                          "type": "object"
                        },
                        "ranked_by": {
                          "type": "string"
                        },
                        "codes": {
                          "type": "array",
                          "items": {
                            "allOf": [
                              {
                                "type": "object",
                                "properties": {
                                  "rank": {
                                    "type": "integer"
                                  }
                                }
                              },
                              {
                                "$ref": "#/components/schemas/ScanSummary"
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/url-safety-checks": {
      "post": {
        "operationId": "checkUrlSafety",
        "summary": "The phishing and malware verdict MyQRx gives every destination, for any URL. Counted against a daily cap per plan.",
        "x-ability": "any",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verdict. `error` means a feed was unreachable: treat the URL as unchecked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string"
                        },
                        "verdict": {
                          "type": "string",
                          "enum": [
                            "clean",
                            "blocked",
                            "error"
                          ]
                        },
                        "safe": {
                          "type": "boolean"
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "note": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "summary": "This document.",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "A token from Settings → API tokens (mqx_…)."
      }
    },
    "responses": {
      "Error": {
        "description": "An error. `code` is stable; `hint` says what to do next.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthenticated",
                  "account_not_activated",
                  "missing_ability",
                  "quota_exceeded",
                  "not_found",
                  "edit_refused",
                  "not_dynamic",
                  "validation_failed",
                  "idempotency_key_reused",
                  "invalid_idempotency_key",
                  "rate_limited",
                  "daily_limit_reached",
                  "method_not_allowed"
                ]
              },
              "message": {
                "type": "string"
              },
              "hint": {
                "type": "string"
              },
              "docs_url": {
                "type": "string"
              },
              "fields": {
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "upgrade_url": {
                "type": "string"
              }
            }
          }
        }
      },
      "CreateCode": {
        "type": "object",
        "description": "Fields depend on `type`, exactly as in the web builder.",
        "properties": {
          "type": {
            "type": "string",
            "default": "short_url",
            "enum": [
              "short_url",
              "google_review",
              "social",
              "whatsapp",
              "location",
              "url",
              "text",
              "vcard",
              "wifi",
              "email",
              "phone",
              "upi"
            ],
            "description": "Dynamic: short_url (default), google_review, social, whatsapp, location. Static (unlimited): url, text, vcard, wifi, email, phone, upi."
          },
          "name": {
            "type": "string",
            "maxLength": 120,
            "description": "Your label; people who scan never see it."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Destination, for short_url and url."
          },
          "custom_code": {
            "type": "string",
            "description": "Choose the tilg.in path (3–32 of a-z 0-9 - _), dynamic types only. Cannot be changed later."
          },
          "text": {
            "type": "string"
          },
          "ssid": {
            "type": "string"
          },
          "encryption": {
            "type": "string",
            "enum": [
              "WPA",
              "WEP",
              "none"
            ]
          },
          "password": {
            "type": "string"
          },
          "hidden": {
            "type": "boolean"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "organization": {
            "type": "string"
          },
          "job_title": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "vcard_url": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "place": {
            "type": "string"
          },
          "network": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "whatsapp": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "location": {
            "type": "string"
          },
          "vpa": {
            "type": "string"
          },
          "payee": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "note": {
            "type": "string"
          },
          "style": {
            "type": "object",
            "description": "Optional design: foreground, background (hex) and the builder's shape, eye, gradient and frame fields. Low-contrast designs are refused."
          }
        }
      },
      "Code": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "dynamic",
              "static"
            ]
          },
          "short_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "destination": {
            "type": [
              "string",
              "null"
            ]
          },
          "safety": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "verdict": {
                "type": "string",
                "enum": [
                  "pending",
                  "clean",
                  "blocked",
                  "error"
                ]
              },
              "checked_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "retired"
            ]
          },
          "content": {
            "type": [
              "object",
              "array",
              "null"
            ],
            "description": "A static code's content."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "links": {
            "type": "object"
          }
        }
      },
      "Share": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "scans": {
            "type": "integer"
          },
          "share": {
            "type": "number"
          }
        }
      },
      "ScanSummary": {
        "type": "object",
        "properties": {
          "code": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "short_url": {
                "type": "string"
              }
            }
          },
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string"
              },
              "to": {
                "type": "string"
              },
              "days": {
                "type": "integer"
              },
              "timezone": {
                "type": "string"
              }
            }
          },
          "scans": {
            "type": "integer"
          },
          "unique_scans": {
            "type": "integer",
            "description": "Summed daily uniques."
          },
          "unique_share": {
            "type": "number"
          },
          "previous_period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string"
              },
              "to": {
                "type": "string"
              },
              "scans": {
                "type": "integer"
              },
              "unique_scans": {
                "type": "integer"
              }
            }
          },
          "change_vs_previous": {
            "type": [
              "number",
              "null"
            ],
            "description": "0.25 = 25% more scans than the previous equal period; null when that had none."
          },
          "top_countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Share"
            }
          },
          "top_devices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Share"
            }
          },
          "top_os": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Share"
            }
          },
          "daily": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "scans": {
                  "type": "integer"
                }
              }
            }
          },
          "peak_hour": {
            "type": "object",
            "properties": {
              "hour": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "0–23, local time."
              },
              "scans": {
                "type": "integer"
              },
              "note": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Why there is no peak hour: only ranges within the last 90 days have one."
              }
            }
          }
        }
      }
    }
  }
}