> For the complete documentation index, see [llms.txt](https://docs.usestencil.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.usestencil.com/integrations/secure-signed-image/signed-image.md).

# Basic

Your image modifications are signed with your project's API token, thus your image can only be modified by you.

{% tabs %}
{% tab title="Elixir" %}

```elixir
def create_signed_image do
  user_id = ""
  secret_key = ""
  base_id = ""
  
  modifications = 
    Jason.encode!([
      %{"name" => "text_1", "text" => "secure image"},
      %{"name" => "rating_2", "rating" => 5}
    ])
    |> Base.url_encoded64(padding: false)
    
  parameters = "#{user_id}+#{base_id}+#{modifications}"
  
  signature =
    :crypto.hmac(:sha256, secret_key, parameters)
    |> Base.encode16()
    |> String.downcase()
    
  "https://images.usestencil.com/signed-images/#{user_id}/#{base_id}.png?modifications=#{modifications}&s=#{signature}"
end
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import hmac
import hashlib
import base64

def base64_encode(string):
    """
    Removes any `=` used as padding from the encoded string.
    """
    encoded = base64.b64encode(string.encode())
    encoded = encoded.rstrip(b'=')
    return encoded

def generate_url():
  user_id = ""
  secret_key = ""
  base_id = ""

  modifications = [
    { "name": "text_1", "text": "secure image" },
    { "name": "rating_2", "rating": 5 },
  ]

  # ensure no spaces in json output
  encoded = json.dumps(modifications, separators=(',', ':'))
  encoded = base64_encode(encoded).decode()

  parameters = "{user_id}+{base_id}+{modifications}".format(user_id=user_id, base_id=base_id, modifications=encoded)

  signature = hmac.new(secret_key.encode(), parameters.encode(), hashlib.sha256).hexdigest()

  url = "https://images.usestencil.com/signed-images/{user_id}/{base_id}.png?modifications={modifications}&s={signature}".format(
      base_id=base_id,
      user_id=user_id,
      modifications=encoded,
      signature=signature
  )
  print(url)

if __name__ == "__main__":
    generate_url()

```

{% endtab %}
{% endtabs %}

### Changing image format on the fly

We support both JPEG and PNG. To use PNG, change the image in the url to `.png` and to use JPEG, change the image in the url to `.jpg`

### Cache

Image is only generated once (synchronously on the first request) and subsequent images are pulled from cache. Images pulled from cache do not count towards your quota.

{% hint style="warning" %}
**Order matters.**&#x20;

In order for cache to work properly, please specify the signature parameter last in the query string i.e. `modifications=<your-modifications>&s=<your-signature>`.
{% endhint %}

#### Cache invalidation

Sometimes it is useful to invalidate cache, you can add `ts=<random number>` to invalidate the cache and a new image will be generated (this will count towards your quota).
