# Render a signed URL

> Render an image from a signed URL that carries the template id, your modifications, and a signature. Bannerify caches the image for up to 7 days.

Canonical: https://bannerify.co/docs/api-reference/endpoint/signed-url

## OpenAPI

```json get /v1/templates/signedurl
{
  "openapi": "3.0.0",
  "info": {
    "title": "Bannerify API",
    "version": "0.0.0"
  },
  "servers": [
    {
      "url": "https://api.bannerify.co",
      "description": "Production"
    }
  ],
  "security": [
    {
      "token": []
    }
  ],
  "paths": {
    "/v1/templates/signedurl": {
      "get": {
        "description": "Render a template as an image. This is the endpoint that a signed URL calls: you build the URL yourself, or with an SDK, and anyone who opens it gets the image. The URL carries the template id, the modifications, and a signature, so nobody can change them. Bannerify caches the image at the edge for up to 7 days. A render counts toward your quota, and a cache hit does not. See https://bannerify.co/docs/essentials/signed-urls for how to build and sign the URL.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "png",
                "jpeg",
                "webp"
              ],
              "default": "png",
              "description": "The image format. The value is case-sensitive, so jpg and JPEG are not valid."
            },
            "required": false,
            "description": "The image format. The value is case-sensitive, so jpg and JPEG are not valid.",
            "name": "format",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "description": "Encoder quality for the lossy formats (jpeg, webp), 1-100. Defaults to 90. Ignored for png.",
              "example": 90
            },
            "required": false,
            "description": "Encoder quality for the lossy formats (jpeg, webp), 1-100. Defaults to 90. Ignored for png.",
            "name": "quality",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string"
                }
              ],
              "description": "By default, Bannerify caches the rendered image at the edge for up to 7 days. Set this to true to skip the cache and render a new image on every request. Every uncached render counts toward your quota.",
              "example": "true"
            },
            "required": false,
            "description": "By default, Bannerify caches the rendered image at the edge for up to 7 days. Set this to true to skip the cache and render a new image on every request. Every uncached render counts toward your quota.",
            "name": "nocache",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string"
                }
              ],
              "description": "Has no effect.",
              "deprecated": true
            },
            "required": false,
            "description": "Has no effect.",
            "name": "_debug",
            "in": "query"
          },
          {
            "schema": {
              "type": "object",
              "properties": {
                "endPoint": {
                  "type": "string",
                  "description": "S3 endpoint URL (without protocol)",
                  "example": "s3.amazonaws.com"
                },
                "port": {
                  "type": "number",
                  "default": 443,
                  "description": "S3 endpoint port",
                  "example": 443
                },
                "useSSL": {
                  "type": "boolean",
                  "default": true,
                  "description": "Whether to use SSL/TLS",
                  "example": true
                },
                "region": {
                  "type": "string",
                  "description": "S3 region",
                  "example": "us-east-1"
                },
                "bucket": {
                  "type": "string",
                  "description": "S3 bucket name",
                  "example": "my-images-bucket"
                },
                "pathStyle": {
                  "type": "boolean",
                  "default": false,
                  "description": "Whether to use path-style URLs",
                  "example": false
                },
                "accessKey": {
                  "type": "string",
                  "description": "S3 access key"
                },
                "secretKey": {
                  "type": "string",
                  "description": "S3 secret key"
                },
                "customUrl": {
                  "type": "string",
                  "description": "Custom URL template for accessing uploaded files. Use {key} as placeholder for the file key.",
                  "example": "https://cdn.example.com/{key}"
                }
              },
              "required": [
                "endPoint",
                "region",
                "bucket",
                "accessKey",
                "secretKey"
              ],
              "description": "Save the file to your own S3-compatible bucket. Without it, Bannerify saves the file in its own storage and serves it from its CDN. Only the stored endpoints use this field, and the Scale and Enterprise plans include it. Other plans get the error code FORBIDDEN."
            },
            "required": false,
            "description": "Save the file to your own S3-compatible bucket. Without it, Bannerify saves the file in its own storage and serves it from its CDN. Only the stored endpoints use this field, and the Scale and Enterprise plans include it. Other plans get the error code FORBIDDEN.",
            "name": "s3Config",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "The id of the template to render. In the dashboard, open the menu on a template card and choose Copy Template ID.",
              "example": "tpl_xxx"
            },
            "required": true,
            "description": "The id of the template to render. In the dashboard, open the menu on a template card and choose Copy Template ID.",
            "name": "templateId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Deprecated alias of apiKeyHashed",
              "deprecated": true
            },
            "required": false,
            "description": "Deprecated alias of apiKeyHashed",
            "name": "apiKeyMd5",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "The SHA-256 hash of your API key, in hex. It tells Bannerify which key signed the URL, and it cannot sign anything. Send it with every request."
            },
            "required": false,
            "description": "The SHA-256 hash of your API key, in hex. It tells Bannerify which key signed the URL, and it cannot sign anything. Send it with every request.",
            "name": "apiKeyHashed",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "The signature, in hex. It is the HMAC-SHA256 of the other query parameters, sorted by name and encoded like URLSearchParams, with your API key as the secret. See https://bannerify.co/docs/essentials/signed-urls."
            },
            "required": true,
            "description": "The signature, in hex. It is the HMAC-SHA256 of the other query parameters, sorted by name and encoded like URLSearchParams, with your API key as the secret. See https://bannerify.co/docs/essentials/signed-urls.",
            "name": "sign",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Modification"
                  }
                },
                {
                  "type": "object",
                  "additionalProperties": {
                    "nullable": true
                  }
                },
                {
                  "type": "string"
                },
                {
                  "nullable": true
                }
              ],
              "default": [],
              "description": "The values to change in the template, as an array of modifications. Each modification names a layer and sets one or more fields, and a layer without a modification keeps the values from the template. An object keyed by layer name and a JSON string of either shape also work. See https://bannerify.co/docs/essentials/modifications.",
              "example": [
                {
                  "name": "headline",
                  "text": "Summer sale"
                },
                {
                  "name": "photo",
                  "src": "https://example.com/photo.png"
                }
              ]
            },
            "required": false,
            "description": "The values to change in the template, as an array of modifications. Each modification names a layer and sets one or more fields, and a layer without a modification keeps the values from the template. An object keyed by layer name and a JSON string of either shape also work. See https://bannerify.co/docs/essentials/modifications.",
            "name": "modifications",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The image file. The Content-Type header tells the format. The RateLimit-Remaining and RateLimit-Reset headers show the state of your rate limit.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The binary of image file"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The binary of image file"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The binary of image file"
                }
              }
            }
          },
          "400": {
            "description": "The request cannot be processed. BAD_REQUEST means a field is invalid, the API key is unknown, or a signed URL has a wrong signature. FETCH_IMAGE_ERROR means Bannerify could not download an image from a src URL. A rate limit on the PDF and stored endpoints also uses status 400 with the code TOO_MANY_REQUESTS. Read error.code to tell them apart.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrBadRequest"
                    },
                    {
                      "$ref": "#/components/schemas/ErrFetchImageError"
                    },
                    {
                      "$ref": "#/components/schemas/ErrTooManyRequests"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The template does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrNotFound"
                }
              }
            }
          },
          "500": {
            "description": "The server could not finish the request. INTERNAL_SERVER_ERROR means an unexpected failure, such as a render error or a failed upload to your S3 bucket. USAGE_EXCEEDED means your workspace reached its daily or monthly generation limit, and RATE_LIMITED means the API key sent too many requests. Read error.code to tell them apart.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ErrInternalServerError"
                    },
                    {
                      "$ref": "#/components/schemas/ErrUsageExceeded"
                    },
                    {
                      "$ref": "#/components/schemas/ErrRateLimited"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ErrBadRequest": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "BAD_REQUEST"
                ],
                "description": "A machine readable error code.",
                "example": "BAD_REQUEST"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/BAD_REQUEST"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrFetchImageError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "FETCH_IMAGE_ERROR"
                ],
                "description": "A machine readable error code.",
                "example": "FETCH_IMAGE_ERROR"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/FETCH_IMAGE_ERROR"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrTooManyRequests": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "TOO_MANY_REQUESTS"
                ],
                "description": "A machine readable error code.",
                "example": "TOO_MANY_REQUESTS"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/TOO_MANY_REQUESTS"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrNotFound": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "NOT_FOUND"
                ],
                "description": "A machine readable error code.",
                "example": "NOT_FOUND"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/NOT_FOUND"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrInternalServerError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "INTERNAL_SERVER_ERROR"
                ],
                "description": "A machine readable error code.",
                "example": "INTERNAL_SERVER_ERROR"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/INTERNAL_SERVER_ERROR"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrUsageExceeded": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "USAGE_EXCEEDED"
                ],
                "description": "A machine readable error code.",
                "example": "USAGE_EXCEEDED"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/USAGE_EXCEEDED"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrRateLimited": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "RATE_LIMITED"
                ],
                "description": "A machine readable error code.",
                "example": "RATE_LIMITED"
              },
              "docs": {
                "type": "string",
                "description": "A link to our documentation with more details about this error code",
                "example": "https://bannerify.co/docs/api-reference/errors/code/RATE_LIMITED"
              },
              "message": {
                "type": "string",
                "description": "A human readable explanation of what went wrong"
              },
              "requestId": {
                "type": "string",
                "description": "Please always include the requestId in your error report",
                "example": "req:1234"
              }
            },
            "required": [
              "code",
              "docs",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Modification": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the layer to change. The match is exact and case-sensitive, and a name that no layer has is ignored.",
            "example": "headline"
          },
          "color": {
            "type": "string",
            "description": "The text color of a text layer, for example #ff5733. Other layers ignore it.",
            "example": "#FF0000"
          },
          "src": {
            "type": "string",
            "description": "The URL of the image for an image layer. It must be a public PNG, JPEG, WebP, or SVG file.",
            "example": "https://example.com/image.jpg"
          },
          "text": {
            "type": "string",
            "description": "The text of a text layer, the body of an alert layer, or the signer name of a signature layer.",
            "example": "Hello World"
          },
          "barcode": {
            "type": "string",
            "description": "The value of a barcode layer.",
            "example": "1234567890"
          },
          "qrcode": {
            "type": "string",
            "description": "The content of a QR code layer.",
            "example": "https://example.com"
          },
          "visible": {
            "type": "boolean",
            "description": "Show or hide the layer. It works on every layer type.",
            "example": true
          },
          "star": {
            "type": "number",
            "description": "The rating of a star layer. A value of 0 is ignored.",
            "example": 5
          },
          "rows": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "value": {
                      "type": "number"
                    },
                    "color": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "label",
                    "value"
                  ]
                },
                "description": "Chart points, one series."
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "value": {
                            "type": "number"
                          },
                          "color": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "label",
                          "value"
                        ]
                      }
                    },
                    "color": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "name",
                    "data"
                  ]
                },
                "description": "Chart series, several series."
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": "boolean"
                      },
                      {
                        "nullable": true
                      }
                    ]
                  }
                },
                "description": "Table rows, keyed by column name."
              },
              {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "value": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        }
                      ]
                    },
                    "valueColor": {
                      "type": "string"
                    },
                    "valueStyle": {
                      "type": "object",
                      "additionalProperties": {
                        "nullable": true
                      }
                    },
                    "keyStyle": {
                      "type": "object",
                      "additionalProperties": {
                        "nullable": true
                      }
                    }
                  },
                  "required": [
                    "key",
                    "value"
                  ]
                },
                "description": "Key value entries."
              }
            ],
            "description": "The rows of a data layer. A table takes row objects keyed by the column names, a chart takes { label, value } points or { name, data } series, and a key value layer takes { key, value } entries.",
            "example": [
              {
                "label": "Jan",
                "value": 22
              }
            ]
          },
          "columns": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string"
                    },
                    "header": {
                      "type": "string"
                    },
                    "align": {
                      "type": "string",
                      "enum": [
                        "left",
                        "center",
                        "right"
                      ]
                    },
                    "width": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        }
                      ]
                    }
                  },
                  "required": [
                    "key",
                    "header"
                  ]
                }
              ]
            },
            "description": "The columns of a table layer, as a column name or as a { key, header, align, width } object",
            "example": [
              "Item",
              "Qty",
              "Total"
            ]
          },
          "title": {
            "type": "string",
            "description": "The heading of an alert layer, or the job title of a signature layer",
            "example": "Payment terms"
          },
          "date": {
            "type": "string",
            "description": "The date of a signature layer",
            "example": "2026-01-31"
          },
          "values": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "description": "The fields of a component layer, by field key, such as { label, rating } for a rating pill. The properties panel of the layer lists its fields. A key that the component does not have is ignored.",
            "example": {
              "label": "Google:",
              "rating": 4.8
            }
          }
        },
        "required": [
          "name"
        ],
        "description": "A change to one layer of the template for one render. A field that does not fit the layer is ignored."
      }
    }
  }
}
```

## Related

- [Previous: Retrieve a video](https://bannerify.co/docs/api-reference/endpoint/get-video)
- [Next: Get workspace info](https://bannerify.co/docs/api-reference/endpoint/info)
- [Documentation index](https://bannerify.co/docs/llms.txt)
