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

# Handling images and asset uploads

> How Snag validates image sources, when external URLs are allowed, and how to upload assets for use in objects like Users, Loyalty Rules, Pages, and Badges etc.

<head>
  <script type="application/ld+json">
    {JSON.stringify({
            "@context": "https://schema.org",
            "@graph": [
              {
                "@type": "TechArticle",
                "headline": "Handling images and asset uploads",
                "description": "How Snag validates image sources, when external URLs are allowed, and how to upload assets for use in objects like Users, Loyalty Rules, Pages, and Badges etc.",
                "author": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/"},
                "publisher": {"@type": "Organization", "name": "Snag Solutions", "url": "https://www.snagsolutions.io/", "logo": {"@type": "ImageObject", "url": "https://assets.snagsolutions.io/public/docs/snag-logo-dark-no-bg.svg"}},
                "mainEntityOfPage": "https://docs.snagsolutions.io/loyalty/handling-image-assets-and-upload"
              },
              {
                "@type": "BreadcrumbList",
                "itemListElement": [
                  {"@type": "ListItem", "position": 1, "name": "Home", "item": "https://docs.snagsolutions.io/welcome"},
                  {"@type": "ListItem", "position": 2, "name": "Loyalty", "item": "https://docs.snagsolutions.io/loyalty/loyalty-overview"},
                  {"@type": "ListItem", "position": 3, "name": "Advanced workflows", "item": "https://docs.snagsolutions.io/loyalty/managing-user-accounts"},
                  {"@type": "ListItem", "position": 4, "name": "Handling image assets and upload"}
                ]
              }
            ]
          })}
  </script>
</head>

<Info>
  This page explains Snag's image/asset handling policy, supported sources, file
  size limits, and the recommended upload flow for use across admin and API. Use
  this when attaching images to objects such as Users, Loyalty Rules, Pages, and
  Badges.
</Info>

### What you can and cannot link

* **Internal assets are required (most fields)**: For images that are uploaded from admin and linked to Snag objects (for example: `UserMetadata.logoUrl`, `LoyaltyRule.mediaUrl`, page builder images, and `LoyaltyBadge.imageUrl`), you must first upload the file using Snag's asset endpoint and then reference the returned asset `url` in your create/update request.
* **External URLs allowed (specific fields only)**:
  * **`UserMetadata.logoUrl`**: May reference external sources (e.g., Twitter).
  * **`LoyaltyPost.mediaUrl`**: May reference external sources (e.g., Twitter and other social platforms).

<Warning>
  For fields that require internal assets, Snag rejects external URLs. Always
  upload via the asset API first and then pass the returned `url`.
</Warning>

### File size limits and validation

* **Images**: up to 1 MB
* **Videos**: up to 10 MB
* You must provide the exact `fileSize` (in bytes) when requesting the presigned URL, and the uploaded file must match that size.

<Note>
  Supported paths include: `announcements`, `images`, `videos`, `banners`,
  `fonts`, `posts`, `profiles`, `minting`, `loyalty`, `tokenClaims`.
</Note>

### The upload flow (recommended)

Use the Asset Upload endpoint to obtain a presigned URL, upload your file to that URL, and then reference the returned public `url` in your object mutation.

<Steps>
  <Step title="Request a presigned upload URL">
    Call the Asset Upload API with the file name, exact file size (in bytes), and desired `filePath`. You'll receive a `signedUrl` (for PUT upload) and a public `url` (to store on your object). See the <a href="https://docs.snagsolutions.io/api-reference/asset/get-asset-upload-url" target="_blank" rel="noreferrer">Asset upload endpoint documentation</a>.
  </Step>

  <Step title="Upload the file to the signed URL (PUT)">
    Perform a `PUT` to `signedUrl` with the file as the request body. Ensure
    `Content-Type` matches the file type and upload before the URL expires.
  </Step>

  <Step title="Use the asset URL in your object create/update">
    Include the returned `url` in the appropriate field. This is required for fields that do not accept external URLs.
  </Step>
</Steps>

### Common validation errors and fixes

<AccordionGroup>
  <Accordion title="400 Bad Request when uploading">
    * **Likely cause**: `fileSize` mismatch between the presigned request and actual uploaded bytes.
    * **Fix**: Recompute the exact file size in bytes and request a new `signedUrl` with the corrected `fileSize`.
  </Accordion>

  <Accordion title="403 Forbidden on PUT to signed URL">
    * **Likely causes**: Expired `signedUrl` or incorrect `Content-Type`. -
      **Fix**: Request a new `signedUrl` and upload immediately; ensure
      `Content-Type` matches the file type (e.g., `image/png`).
  </Accordion>

  <Accordion title="Field rejects external URL">
    * **Cause**: The target field only accepts Snag-hosted assets. - **Fix**: Use
      the upload flow above and pass the returned `url` instead of an external link.
  </Accordion>

  <Accordion title="Which fields can use external URLs?">
    * **Allowed**: `UserMetadata.logoUrl`, `LoyaltyPost.mediaUrl` (can reference external sources like Twitter and other social platforms).
    * **Not allowed**: Most admin-uploadable fields such as `UserMetadata.logoUrl`, `LoyaltyRule.mediaUrl`, page builder images, and `LoyaltyBadge.imageUrl`.
  </Accordion>
</AccordionGroup>

### Admin UI behavior

When you upload images from the admin UI for fields that require internal assets, Snag uses the same flow under the hood: it requests a presigned URL, uploads your file, and stores the resulting asset `url` on the object. Pasting external URLs into these fields is blocked.

### API reference

See the official endpoint for complete parameters, supported paths, and responses:

* **Asset Upload**: <a href="/api-reference/asset/get-asset-upload-url" target="_blank" rel="noreferrer">Get Asset upload URL</a>

<Check>
  You now know when external URLs are allowed and how to upload images to Snag
  and attach them to your objects safely.
</Check>
