Skip to main content
NoCloud uses a two-step upload process:
  1. Get a signed URL from the Generate Signed URL endpoint
  2. Upload the file directly to the signed URL using a PUT request
This approach allows for efficient, direct-to-storage uploads without proxying through your backend.

Complete Upload Example

# Step 1: Get signed URL
SIGNED_URL_RESPONSE=$(curl -s -X GET \
  "https://api.nonefivem.com/cloud/storage/signed-url?contentType=image/png&size=1048576" \
  -H "Authorization: Bearer your-api-key")

# Extract the signed URL

UPLOAD_URL=$(echo $SIGNED_URL_RESPONSE | jq -r '.url')

# Step 2: Upload file to signed URL

curl -X PUT "$UPLOAD_URL" \
 -H "Content-Type: image/png" \
 --data-binary @./screenshot.png

Upload with Metadata

Attach custom metadata to track file ownership or categorization:
const params = new URLSearchParams({
  contentType: "image/png",
  size: file.size.toString(),
  // Metadata as JSON string
  metadata: JSON.stringify({
    oderId: "order_123",
    visitorId: 456,
    isPublic: true
  })
});

const response = await fetch(
  `https://api.nonefivem.com/cloud/storage/signed-url?${params}`,
  {
    headers: { Authorization: "Bearer your-api-key" }
  }
);
Metadata constraints: - Maximum 10 keys - Key names: alphanumeric, underscores, hyphens only (max 128 chars) - Values: string (max 512 chars), number, or boolean - Total metadata size: max 2KB

Upload with Progress Tracking

For larger files, you may want to track upload progress:
async function uploadWithProgress(file, onProgress) {
  // Get signed URL first...
  const { url, mediaId, mediaUrl } = await getSignedUrl(file);

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();

    xhr.upload.addEventListener("progress", (event) => {
      if (event.lengthComputable) {
        const percentComplete = (event.loaded / event.total) * 100;
        onProgress(percentComplete);
      }
    });

    xhr.addEventListener("load", () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve({ mediaId, mediaUrl });
      } else {
        reject(new Error(`Upload failed with status ${xhr.status}`));
      }
    });

    xhr.addEventListener("error", () => reject(new Error("Upload failed")));

    xhr.open("PUT", url);
    xhr.setRequestHeader("Content-Type", file.type);
    xhr.send(file);
  });
}

// Usage
await uploadWithProgress(file, (progress) => {
  console.log(`Upload progress: ${progress.toFixed(1)}%`);
});
Signed URLs expire after 15 minutes. Generate a new URL if the upload doesn’t complete in time.