Redirect Rules

Intercept storefront 404s with priority-ordered glob redirects

GET /admin/api/redirect-rules

List every redirect rule, ordered by priority ascending. Includes hit count and last-match timestamp for each rule.

GET /admin/api/redirect-rules/{id}

Fetch a single redirect rule by id.

POST /admin/api/redirect-rules

Create a redirect rule. Patterns may use glob segments (e.g. /products/**) and the destination may reference capture groups via ${1}, ${2}, …

PUT /admin/api/redirect-rules/{id}

Partial update of a redirect rule. Any field may be omitted; omitted fields are left unchanged. The combined (pattern, destination) shape is re-validated after the overlay.

DELETE /admin/api/redirect-rules/{id}

Permanently delete a redirect rule.

POST /admin/api/redirect-rules/test

Dry-run the current rule set against a candidate path without recording a hit. Returns the first matching rule's substituted destination, status code, and id, or `matched: false` if nothing matches.

POST /admin/api/redirect-rules/scan-loops

Scan the enabled rule set for redirect chains that loop back on themselves. Each rule is given a probe path and the matcher is walked forward until a previously seen rule fires, signalling a cycle. Read-only — records no hits and changes no rules. Returns an empty `cycles` array when the rule set is loop-free.

POST /admin/api/redirect-rules/auto-fix-loops

Break detected redirect loops by disabling the leading rule (lowest priority, ties broken by lowest id) of each supplied cycle. Two-step: send `confirm: false` (the default) for a dry-run preview of which rules would be disabled, then resend with `confirm: true` to execute. Pass the `cycles` returned by scan-loops; the server re-detects and aborts with 409 if the loop set has changed since you scanned.

GET /admin/api/redirect-rules

List every redirect rule, ordered by priority ascending. Includes hit count and last-match timestamp for each rule.

Response 200

[
  {
    "id": 1,
    "pattern": "/products/**",
    "destination": "/p/${1}",
    "status_code": 301,
    "priority": 100,
    "enabled": true,
    "hit_count": 42,
    "last_hit_at": "2026-05-02T18:30:00Z",
    "created_at": "2026-04-15T09:00:00Z",
    "updated_at": "2026-04-15T09:00:00Z"
  },
  {
    "id": 2,
    "pattern": "/old-blog",
    "destination": "https://blog.example.com",
    "status_code": 302,
    "priority": 200,
    "enabled": false,
    "hit_count": 0,
    "last_hit_at": null,
    "created_at": "2026-04-20T11:15:00Z",
    "updated_at": "2026-04-20T11:15:00Z"
  }
]

Example

curl "https://yourshop.zeroshop.io/admin/api/redirect-rules" \
  -H "Authorization: Bearer zspat_..."
GET /admin/api/redirect-rules/{id}

Fetch a single redirect rule by id.

Response 200

{
  "id": 1,
  "pattern": "/products/**",
  "destination": "/p/${1}",
  "status_code": 301,
  "priority": 100,
  "enabled": true,
  "hit_count": 42,
  "last_hit_at": "2026-05-02T18:30:00Z",
  "created_at": "2026-04-15T09:00:00Z",
  "updated_at": "2026-04-15T09:00:00Z"
}

Example

curl "https://yourshop.zeroshop.io/admin/api/redirect-rules/1" \
  -H "Authorization: Bearer zspat_..."
POST /admin/api/redirect-rules

Create a redirect rule. Patterns may use glob segments (e.g. /products/**) and the destination may reference capture groups via ${1}, ${2}, …

Request Body

{
  "pattern": "/products/**",
  "destination": "/p/${1}",
  "status_code": 301,
  "priority": 100,
  "enabled": true
}

Response 201

{
  "id": 7,
  "pattern": "/products/**",
  "destination": "/p/${1}",
  "status_code": 301,
  "priority": 100,
  "enabled": true,
  "hit_count": 0,
  "last_hit_at": null,
  "created_at": "2026-05-03T10:00:00Z",
  "updated_at": "2026-05-03T10:00:00Z"
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/redirect-rules" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{
    "pattern": "/products/**",
    "destination": "/p/${1}",
    "status_code": 301,
    "priority": 100
  }'
PUT /admin/api/redirect-rules/{id}

Partial update of a redirect rule. Any field may be omitted; omitted fields are left unchanged. The combined (pattern, destination) shape is re-validated after the overlay.

Request Body

{
  "enabled": false
}

Response 200

{
  "id": 1,
  "pattern": "/products/**",
  "destination": "/p/${1}",
  "status_code": 301,
  "priority": 100,
  "enabled": false,
  "hit_count": 42,
  "last_hit_at": "2026-05-02T18:30:00Z",
  "created_at": "2026-04-15T09:00:00Z",
  "updated_at": "2026-05-03T10:05:00Z"
}

Example

curl -X PUT "https://yourshop.zeroshop.io/admin/api/redirect-rules/1" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'
DELETE /admin/api/redirect-rules/{id}

Permanently delete a redirect rule.

Response 204

Example

curl -X DELETE "https://yourshop.zeroshop.io/admin/api/redirect-rules/1" \
  -H "Authorization: Bearer zspat_..."
POST /admin/api/redirect-rules/test

Dry-run the current rule set against a candidate path without recording a hit. Returns the first matching rule's substituted destination, status code, and id, or `matched: false` if nothing matches.

Request Body

{
  "path": "/products/red-shoes",
  "query": "color=red"
}

Response 200

{
  "matched": true,
  "destination": "/p/red-shoes?color=red",
  "status_code": 301,
  "rule_id": 1
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/redirect-rules/test" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{"path": "/products/red-shoes", "query": "color=red"}'
POST /admin/api/redirect-rules/scan-loops

Scan the enabled rule set for redirect chains that loop back on themselves. Each rule is given a probe path and the matcher is walked forward until a previously seen rule fires, signalling a cycle. Read-only — records no hits and changes no rules. Returns an empty `cycles` array when the rule set is loop-free.

Response 200

{
  "cycles": [
    {
      "rule_ids": [
        5,
        12
      ],
      "hops": [
        {
          "rule_id": 5,
          "matched_path": "/a",
          "resolved_destination": "/b"
        },
        {
          "rule_id": 12,
          "matched_path": "/b",
          "resolved_destination": "/a"
        }
      ]
    }
  ]
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/redirect-rules/scan-loops" \
  -H "Authorization: Bearer zspat_..."
POST /admin/api/redirect-rules/auto-fix-loops

Break detected redirect loops by disabling the leading rule (lowest priority, ties broken by lowest id) of each supplied cycle. Two-step: send `confirm: false` (the default) for a dry-run preview of which rules would be disabled, then resend with `confirm: true` to execute. Pass the `cycles` returned by scan-loops; the server re-detects and aborts with 409 if the loop set has changed since you scanned.

Request Body

{
  "confirm": true,
  "cycles": [
    {
      "rule_ids": [
        5,
        12
      ],
      "hops": [
        {
          "rule_id": 5,
          "matched_path": "/a",
          "resolved_destination": "/b"
        },
        {
          "rule_id": 12,
          "matched_path": "/b",
          "resolved_destination": "/a"
        }
      ]
    }
  ]
}

Response 200

{
  "confirmed": true,
  "disabled_rules": [
    {
      "id": 5,
      "pattern": "/a",
      "destination": "/b",
      "priority": 1
    }
  ]
}

Example

curl -X POST "https://yourshop.zeroshop.io/admin/api/redirect-rules/auto-fix-loops" \
  -H "Authorization: Bearer zspat_..." \
  -H "Content-Type: application/json" \
  -d '{
    "confirm": true,
    "cycles": [
      {
        "rule_ids": [5, 12],
        "hops": [
          {"rule_id": 5, "matched_path": "/a", "resolved_destination": "/b"},
          {"rule_id": 12, "matched_path": "/b", "resolved_destination": "/a"}
        ]
      }
    ]
  }'

We value your privacy

We use cookies for essential site functionality and, with your consent, analytics to understand how our platform is used. No personal data is shared with third parties. See our Privacy Policy for details.