> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useqrkit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create QR Code

> Creates a QR code, renders its image (SVG, stored on QRKit's CDN) and
returns the full object. Requires the `qr:write` scope.

Each target type takes structured fields (recommended) or a
pre-encoded `destination` string. Dynamic codes count against your
plan's QR limit; static codes do not.




## OpenAPI

````yaml POST /qr-codes
openapi: 3.0.3
info:
  title: QRKit API
  description: >
    Programmatic access to QRKit — create, manage and track QR codes.


    All requests are scoped to the **workspace** the API key belongs to:

    QR codes and folders created here appear in the QRKit dashboard and

    vice-versa.


    ## Dynamic vs static


    - **Dynamic** codes encode a short scan URL
    (`https://scan.useqrkit.com/{short_code}`)
      that redirects to your destination — editable later, with scan analytics.
      Types: `url` (default), `vcard`.
    - **Static** codes encode their payload directly — nothing to host, but
      the content is fixed once printed and there are no analytics.
      Types: `wifi`, `email`, `sms`, `text`, `event` (always static), and
      `url` when created with `"type": "static"`.

    Dynamic codes have ids like `qr_123`; static codes `sqr_123`.
  version: '2026-02-08'
  contact:
    email: support@useqrkit.com
servers:
  - url: https://api.useqrkit.com/v1
    description: Production
  - url: http://localhost:3001/v1
    description: Local development
security:
  - bearerAuth: []
tags:
  - name: Webhooks
    description: >
      Webhook endpoints receive HTTP POST notifications whenever events occur in
      your workspace.

      Each endpoint subscribes to one or more event types (or `*` for all).
      Requires API key

      scopes `webhooks:read` and/or `webhooks:write`.


      Endpoint caps are counted per `source`: up to 10 endpoints created
      directly

      (`source: "api"`, the default) plus up to 50 for each integration platform

      (`zapier`, `make`, `n8n`) — integrations register one endpoint per active

      Zap/scenario and manage their own.


      ## Event catalog


      | Type | Fires when |

      |------|-----------|

      | `qr.created` | A QR code is created via API or dashboard |

      | `qr.updated` | A QR code's name, target, design, tags or active state
      changes |

      | `qr.deleted` | A QR code is deleted |

      | `qr.scanned` | A dynamic QR code is scanned (see privacy guarantee
      below) |

      | `batch.completed` | All items in a batch job have finished processing |

      | `batch.failed` | A batch job failed globally (not item-level errors) |

      | `ping` | Emitted only via `POST /webhooks/{id}/test` to verify
      connectivity |


      ## Event envelope


      Every delivery sends a JSON body with the shape:


      ```json

      {
        "id": "evt_…",
        "object": "event",
        "type": "qr.scanned",
        "created_at": "2026-06-11T12:34:56.789Z",
        "data": { … }
      }

      ```


      Events are addressable via `GET /v1/events/{id}` for 30 days after
      creation.

      Re-fetch authoritative object state via the REST API if you need fields
      beyond what

      the event payload includes — event ordering is not guaranteed.


      ## Signature verification


      Every delivery includes the header:


      ```

      QRKit-Signature: t=1718099696,v1=a3b2c1…

      ```


      The signed string is `"{t}.{raw_request_body}"`. Compute `HMAC-SHA256`
      over that

      string using your endpoint secret (`whsec_…`). Compare the result against
      the `v1=`

      value using a **constant-time** equality function. Reject deliveries where
      the

      timestamp `t` differs from your server clock by more than **5 minutes** to
      prevent

      replay attacks.


      During the 24-hour grace period after a `rotate-secret` call the header
      will contain

      two `v1=` entries (one for each secret). Accept a delivery if **either**
      value matches.


      ## Retry schedule


      Failed deliveries (non-2xx response, timeout, or connection error) are
      retried up to

      7 attempts on an exponential back-off schedule:


      | Attempt | Delay after previous |

      |---------|---------------------|

      | 1 | immediate |

      | 2 | 1 minute |

      | 3 | 5 minutes |

      | 4 | 30 minutes |

      | 5 | 2 hours |

      | 6 | 6 hours |

      | 7 | 24 hours |


      After the 7th failed attempt the delivery moves to terminal `failed`
      status (~33 hours

      total window).


      ## Auto-disable


      After sustained delivery failures the endpoint is automatically set to
      `auto_disabled`

      and an email is sent to the workspace owner. Re-enable it by calling

      `PATCH /v1/webhooks/{id}` with `{ "status": "enabled" }` once your server
      is healthy

      (consecutive failure count resets to zero on re-enable).


      ## Delivery semantics


      - **At-least-once** — the same event may be delivered more than once;
      deduplicate by
        the event `id`.
      - **30-day retention** — events and delivery records are kept for 30 days.

      - **Ordering not guaranteed** — process events idempotently and re-fetch
      authoritative
        state via REST or `GET /v1/events` when you need a consistent view.

      ## qr.scanned privacy guarantee


      Scan events delivered to external endpoints contain **no IP address**,
      **no precise

      location** (no latitude, longitude, or postal code), and **no raw
      user-agent string**.

      The `data` payload is limited to: `country`, `city`, `device`, `os`,
      `browser`,

      `referrer`, and `language`.
  - name: Events
    description: >
      Read-only access to the event archive. Events are stored for 30 days
      regardless of

      whether any webhook endpoints are registered. Use this log to replay
      missed deliveries,

      audit activity, or inspect the exact payload QRKit would have sent.
      Requires the

      `webhooks:read` scope.
  - name: Account
    description: >
      Identify the workspace behind an API key. `GET /me` requires no scope —
      any valid

      key works — making it the connection-test endpoint for integrations
      (Zapier, Make,

      n8n) and a quick way to check which plan, scopes and environment a key
      carries.
paths:
  /qr-codes:
    post:
      tags:
        - QR Codes
      summary: Create QR Code
      description: |
        Creates a QR code, renders its image (SVG, stored on QRKit's CDN) and
        returns the full object. Requires the `qr:write` scope.

        Each target type takes structured fields (recommended) or a
        pre-encoded `destination` string. Dynamic codes count against your
        plan's QR limit; static codes do not.
      operationId: createQrCode
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQrCodeRequest'
            examples:
              dynamic-url:
                summary: Dynamic URL code with UTM tracking
                value:
                  name: Summer campaign
                  target:
                    type: url
                    destination: https://example.com/landing
                    utm:
                      source: print
                      medium: qr
                      campaign: summer
              static-wifi:
                summary: Static WiFi code
                value:
                  name: Office WiFi
                  target:
                    type: wifi
                    ssid: OfficeNet
                    password: hunter2
                    encryption: WPA
              vcard:
                summary: Digital business card
                value:
                  name: Jane's card
                  target:
                    type: vcard
                    vcard:
                      your_name: Jane Doe
                      company_name: Acme
                      position: CEO
                      email: jane@acme.com
                      phone_mobile: '+40712345678'
      responses:
        '201':
          description: QR code created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QrCode'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/Unprocessable'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    CreateQrCodeRequest:
      type: object
      required:
        - target
      properties:
        name:
          type: string
          maxLength: 255
          description: Defaults to "Campaign {code}" / "Static {code}".
        type:
          type: string
          enum:
            - dynamic
            - static
          description: >-
            Only meaningful for `url` targets — wifi/email/sms/text/event are
            always static, vcard always dynamic. Omit to use the type's default.
        target:
          $ref: '#/components/schemas/Target'
        design:
          $ref: '#/components/schemas/Design'
        folder_id:
          type: string
          description: Folder to place the code in (e.g. `fld_12`).
        tags:
          type: array
          items:
            type: string
            maxLength: 50
          maxItems: 10
    QrCode:
      type: object
      properties:
        id:
          type: string
          example: qr_123
          description: '`qr_…` for dynamic, `sqr_…` for static.'
        object:
          type: string
          example: qr_code
        kind:
          type: string
          enum:
            - dynamic
            - static
        name:
          type: string
        type:
          type: string
          enum:
            - url
            - text
            - email
            - sms
            - wifi
            - event
            - vcard
        short_code:
          type: string
          example: AB3D5
        short_url:
          type: string
          nullable: true
          example: https://scan.useqrkit.com/AB3D5
          description: null for static codes.
        target:
          type: object
          properties:
            type:
              type: string
            destination:
              type: string
              description: >-
                The stored payload (URL for dynamic url codes, encoded payload
                for statics).
        design:
          type: object
          nullable: true
          properties:
            colors:
              type: object
              properties:
                foreground:
                  type: string
                background:
                  type: string
            gradient:
              type: object
              nullable: true
              properties:
                start:
                  type: string
                end:
                  type: string
                type:
                  type: string
            shapes:
              type: object
              properties:
                body:
                  type: string
                eye_frame:
                  type: string
                eye_ball:
                  type: string
            eye_colors:
              type: object
              nullable: true
              properties:
                frame:
                  type: string
                ball:
                  type: string
            has_logo:
              type: boolean
            transparent_background:
              type: boolean
        folder_id:
          type: string
          nullable: true
          example: fld_12
        tags:
          type: array
          items:
            type: string
        image_url:
          type: string
          example: https://img.useqrkit.com/qr-AB3D5-1717000000000.svg
        download_url:
          type: string
          example: /v1/qr-codes/qr_123/download
        is_active:
          type: boolean
        scan_count:
          type: integer
          nullable: true
          description: null for static codes (no tracking).
        utm:
          type: object
          nullable: true
          description: UTM parameters (null unless set on a dynamic url code).
          properties:
            source:
              type: string
            medium:
              type: string
            campaign:
              type: string
            term:
              type: string
            content:
              type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Target:
      type: object
      required:
        - type
      description: |
        What the QR code points to. Each type takes structured fields, or a
        pre-encoded `destination` string if you prefer to encode yourself.
      properties:
        type:
          type: string
          enum:
            - url
            - text
            - email
            - sms
            - wifi
            - event
            - vcard
        destination:
          type: string
          description: >-
            url: the link. Other types: optional pre-encoded payload (e.g.
            `WIFI:T:WPA;S:…;;`).
        text:
          type: string
          description: 'text: the content to encode.'
        email:
          type: string
          description: 'email: recipient address.'
        subject:
          type: string
          description: 'email: subject line.'
        message:
          type: string
          description: 'email/sms: body text.'
        phone:
          type: string
          description: 'sms: phone number.'
        ssid:
          type: string
          description: 'wifi: network name.'
        password:
          type: string
          description: 'wifi: network password.'
        encryption:
          type: string
          enum:
            - WPA
            - WEP
            - nopass
          description: 'wifi: security type (default WPA).'
        title:
          type: string
          description: 'event: event title.'
        location:
          type: string
          description: 'event: venue.'
        start:
          type: string
          description: 'event: start datetime (ISO 8601).'
        end:
          type: string
          description: 'event: end datetime (ISO 8601).'
        vcard:
          $ref: '#/components/schemas/Vcard'
        utm:
          $ref: '#/components/schemas/Utm'
    Design:
      type: object
      description: >-
        Visual customization. Stored with the code, so the dashboard shows
        exactly what the API created.
      properties:
        error_correction:
          type: string
          enum:
            - L
            - M
            - Q
            - H
          default: Q
          description: >-
            Error-correction level. QRKit renders at Q (25% recovery) by
            default, like the dashboard.
        colors:
          type: object
          properties:
            foreground:
              type: string
              example: '#000000'
            background:
              type: string
              example: '#ffffff'
        gradient:
          type: object
          description: Foreground gradient (overrides colors.foreground).
          properties:
            start:
              type: string
              example: '#005ae0'
            end:
              type: string
              example: '#00c2ff'
            type:
              type: string
              enum:
                - linear-top-to-bottom
                - linear-left-to-right
                - linear-top-left-to-bottom-right
                - linear-top-right-to-bottom-left
                - radial
              default: linear-top-to-bottom
        shapes:
          type: object
          properties:
            body:
              type: string
              enum:
                - square
                - dots
                - rounded
                - extra-rounded
                - classy
                - classy-rounded
              default: square
            eye_frame:
              type: string
              enum:
                - square
                - dot
                - extra-rounded
              default: square
            eye_ball:
              type: string
              enum:
                - square
                - dot
                - extra-rounded
              default: square
        eye_colors:
          type: object
          description: Custom colors for the corner eyes.
          properties:
            frame:
              type: string
              example: '#005ae0'
            ball:
              type: string
              example: '#004bb8'
        logo:
          type: object
          properties:
            url:
              type: string
              format: uri
              description: >-
                http(s) image URL (PNG/JPG/SVG, max 2MB). Fetched once and
                embedded.
            clear_background:
              type: boolean
              default: false
              description: Hide QR dots behind the logo.
        transparent_background:
          type: boolean
          default: false
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - invalid_request
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - unprocessable_entity
                - rate_limit_exceeded
                - internal_error
            message:
              type: string
            doc_url:
              type: string
            request_id:
              type: string
    Vcard:
      type: object
      required:
        - your_name
      properties:
        your_name:
          type: string
        company_name:
          type: string
        position:
          type: string
        website:
          type: string
        phone_mobile:
          type: string
        phone_work:
          type: string
        phone:
          type: string
        fax:
          type: string
        email:
          type: string
        country:
          type: string
        state:
          type: string
        street:
          type: string
        city:
          type: string
        zipcode:
          type: string
        personal_desc:
          type: string
    Utm:
      type: object
      description: UTM parameters baked into the destination URL (dynamic url codes).
      properties:
        source:
          type: string
        medium:
          type: string
        campaign:
          type: string
        term:
          type: string
        content:
          type: string
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Missing scope or plan does not include this feature
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unprocessable:
      description: Request understood but cannot be processed (e.g. plan limit reached)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Rate limit or monthly quota exceeded — see Retry-After header
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key in the Authorization header: `Bearer qr_live_…` or `Bearer
        qr_test_…`. Token endpoints take a Clerk session JWT instead.

````