agentsclimarketplace

Firefly generate image v3 async

Skill Focus-GTS/firefly-services-skills/plugins/firefly-services/skills/firefly-generate-image-v3-async

Production-grade Claude Code skills for Adobe Firefly Services — credentials, generation (V3 async), custom models, expand/fill, video, Photoshop API, Lightroom API. Built by FocusGTS from real enterprise FDE work.

Install
npx -y skills add Focus-GTS/firefly-services-skills --skill firefly-generate-image-v3-async

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Generate images with the Adobe Firefly V3 asynchronous API — job submission, status polling, webhook callbacks, prompt structure, content class, style and structure references, seed control, multi-variation results, and the migration from V2 sync to V3 async. Use whenever the user wants to "generate an image with Firefly", "text-to-image", "Firefly V3", "async generate", "polling", "jobId", "statusUrl", or upgrades from V2 sync. Returns the production pattern for the highest-volume Firefly workload — including the polling cadence that does not get rate-limited and the webhook pattern that scales to thousands of concurrent jobs.

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

13.8 KB, as published. Nobody here has run it

Firefly Generate Image — V3 Async

The production pattern for text-to-image generation with Adobe Firefly's V3 asynchronous API. V3 async is the right shape for every workload above one-off interactive use. The synchronous V2 endpoint is still available but its 30-second timeout and per-request blocking make it unsuitable for production volume.

When to Use This Skill

Use this skill when:

  • Generating images from text prompts at any production volume
  • Migrating from V2 sync (/v2/images/generate) to V3 async (/v3/images/generate)
  • Building a campaign pipeline, banner-at-scale system, or batch generator
  • Adding style or structure references to a generate call
  • Designing a webhook-based generation pipeline

Do NOT use this skill when:

  • The user wants a variation of an existing image — use firefly-generate-similar
  • The user wants to extend an image canvas — use firefly-expand-fill
  • The user is generating video — use firefly-video-model

Sync vs Async — When to Use Which

PropertyV2 sync (/v2/images/generate)V3 async (/v3/images/generate)
Latency to first byte10-30s (blocking)~200ms (returns jobId)
Time to resultSameSame
Connection lifetimeWhole jobJust submission
Resilient to caller restartsNo — results lost on disconnectYes — pick up by jobId
Webhook callbacksNoYes (preferred)
Recommended for productionNoYes
Recommended for one-shot CLIAcceptableAcceptable

Default to V3 async for everything. The only acceptable reason to use V2 sync is a one-shot script where the user is watching the terminal.

The Async Workflow

1. POST /v3/images/generate  →  { jobId, statusUrl, cancelUrl }
2. Either:
   a. Poll statusUrl every 1-2s until status === "succeeded" | "failed"
   b. OR provide a webhook callback URL — Firefly calls it on completion
3. On success: response includes outputs[].image.url (pre-signed)
4. Download from URL within ~1 hour or it expires

Step 1 — Submit the Generation Job

Minimum required request:

curl --silent -X POST 'https://firefly-api.adobe.io/v3/images/generate' \
  -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
  -H "X-Api-Key: $FIREFLY_SERVICES_CLIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "a single red apple on a white background",
    "contentClass": "photo",
    "numVariations": 1,
    "size": {"width": 1024, "height": 1024}
  }'

Response:

{
  "jobId": "urn:ff:jobs:eso851211:86ffe2ea-d765-4bd3-b2fd-568ca8fc36ac",
  "statusUrl": "https://firefly-api.adobe.io/v3/status/urn:ff:jobs:...",
  "cancelUrl": "https://firefly-api.adobe.io/v3/cancel/urn:ff:jobs:..."
}

The submission returns in ~200ms regardless of job complexity. Persist the jobId immediately — that is the only thing that lets you recover the result if the worker crashes mid-poll.

Step 2 — Request Shape

Full request shape with all common fields:

{
  "prompt": "string (required, 1-1024 chars)",
  "negativePrompt": "string (optional, things to avoid)",
  "contentClass": "photo | art",
  "numVariations": 1,
  "size": {"width": 1024, "height": 1024},
  "seeds": [12345],
  "visualIntensity": 6,
  "style": {
    "presets": ["bold_colors"],
    "imageReference": {"source": {"uploadId": "abc-123"}},
    "strength": 75
  },
  "structure": {
    "imageReference": {"source": {"url": "https://..."}},
    "strength": 50
  },
  "customModelId": "optional-uuid-for-custom-model"
}

Supported sizes (V3)

DimensionsAspect
1024×1024Square (1:1)
2048×2048Square (1:1)
2304×1792Landscape (4:3)
1344×768Landscape (7:4)
1152×896Landscape (9:7)
2688×1536Widescreen (16:9)
1792×2304Portrait (3:4)
896×1152Portrait (7:9)

Other dimensions are rejected with a 400. Pick from this list, or generate at the nearest match and crop in post.

Content class

ValueUse for
photoPhotorealistic output — products, scenes, people
artStylized output — illustrations, paintings, designs

Defaults to a balanced output; setting explicitly produces sharper results in the chosen direction.

Variations and seeds

  • numVariations: 1-4. Production typically uses 2-4 to give downstream selection logic options.
  • seeds: array of integers. Same seed + same prompt + same model = deterministic output. Use seeds for A/B testing or reproducibility audits.

Step 3 — Poll for Completion

JOB_ID=$(echo "$SUBMIT_RESPONSE" | jq -r .jobId)
STATUS_URL=$(echo "$SUBMIT_RESPONSE" | jq -r .statusUrl)

while true; do
  RESPONSE=$(curl --silent "$STATUS_URL" \
    -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
    -H "X-Api-Key: $FIREFLY_SERVICES_CLIENT_ID")
  STATUS=$(echo "$RESPONSE" | jq -r .status)
  case "$STATUS" in
    succeeded|failed) echo "$RESPONSE"; break ;;
    *) sleep 1 ;;
  esac
done

Node implementation:

async function pollJob(statusUrl, accessToken, clientId, { intervalMs = 1000, maxMs = 300_000 } = {}) {
  const start = Date.now();
  while (Date.now() - start < maxMs) {
    const res = await fetch(statusUrl, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'X-Api-Key': clientId,
      },
    });
    if (!res.ok) throw new Error(`Status check failed: ${res.status}`);
    const data = await res.json();
    if (data.status === 'succeeded' || data.status === 'failed') return data;
    await new Promise(r => setTimeout(r, intervalMs));
  }
  throw new Error('Job polling timed out');
}

Polling cadence

CadenceWhen to use
1sInteractive workloads, user is waiting
2sBackground batch jobs, no user attention
5sVery large batches where polling rate matters more than latency

Polling every 250ms or faster is wasteful — typical Firefly V3 jobs complete in 3-10 seconds. Sub-second polling will not make them complete faster.

Polling does not consume generation quota

Status calls are billed and rate-limited separately from generation. You can poll aggressively without burning your generation rate limit. Production worry is wasted compute, not quota.

Step 4 — Webhook Callbacks (Preferred at Scale)

Illustrative — verify against current Adobe docs. The notify/webhookUrl/X-Adobe-Signature HMAC fields below are not part of the published Firefly generate-image request schema or the official SDK at time of writing. Treat this section as a design pattern, not a documented contract: confirm field names, headers, and signature scheme against the current Adobe Firefly Services documentation before relying on it. If your account does not expose webhook callbacks, use the polling pattern in Step 3.

For production batch workloads, webhooks beat polling. The pattern is: pass a callback URL on submission and Firefly calls it when the job completes.

{
  "prompt": "...",
  "notify": {
    "webhookUrl": "https://api.example.com/firefly/callback",
    "secretKey": "shared-secret-for-hmac-validation"
  }
}

Adobe POSTs to the webhook URL with the job result body. Validate the HMAC signature in the X-Adobe-Signature header before trusting the payload.

Webhook pattern requires:

ComponentDetail
Public URLReachable from Adobe IP ranges
HMAC validationSHA-256 over the body with the shared secret
IdempotencyAdobe may retry; jobs should be keyed by jobId
Acknowledge with 2xxWithin 30s; otherwise Adobe retries

If the webhook fails repeatedly, Adobe falls back to making the result retrievable via the original statusUrl. Always implement polling fallback for robustness.

Step 5 — Read the Result

A succeeded job's response:

{
  "status": "succeeded",
  "jobId": "urn:ff:jobs:...",
  "result": {
    "size": {"width": 1024, "height": 1024},
    "outputs": [
      {
        "seed": 12345,
        "image": {
          "url": "https://pre-signed-cdn-url..."
        }
      }
    ]
  }
}

Download immediately. The image.url is a pre-signed CDN URL that typically expires within 1 hour. For production:

const result = await pollJob(statusUrl, token, clientId);
for (const output of result.result.outputs) {
  const imgRes = await fetch(output.image.url);
  const buffer = await imgRes.arrayBuffer();
  // Persist to your own bucket
  await s3.putObject({
    Bucket: 'my-outputs',
    Key: `${jobId}/${output.seed}.png`,
    Body: Buffer.from(buffer),
    ContentType: 'image/png',
  });
}

Never store the raw Firefly URL long-term. Always re-host in your own storage.

Style and Structure References

Both V3 image generation supports two reference types:

ReferenceEffect
style.imageReferenceOutput matches the visual style of the reference
style.presetsOutput matches a named style preset
structure.imageReferenceOutput matches the composition of the reference

Combine for fine control:

{
  "prompt": "a futuristic city at sunset",
  "contentClass": "art",
  "style": {
    "presets": ["bold_colors"],
    "imageReference": {"source": {"uploadId": "style-ref-id"}},
    "strength": 75
  },
  "structure": {
    "imageReference": {"source": {"uploadId": "structure-ref-id"}},
    "strength": 50
  }
}

strength 0-100. Higher = stronger influence. Start at 50 and tune.

The reference image must be a valid storage reference — see firefly-services-storage-refs.

Custom Models

To generate with a custom-trained model:

{
  "prompt": "an icon of a key in our brand style",
  "customModelId": "00000000-0000-0000-0000-000000000000",
  "contentClass": "art",
  "size": {"width": 1024, "height": 1024}
}

Custom model IDs come from the custom-model training workflow — see firefly-custom-models.

Production Patterns

Pattern: Single-job CLI

For interactive one-shot use, submit + poll in a single function. Acceptable for <50 calls.

Pattern: Queue-fronted batch

For >50 calls, use the SQS / Lambda / Token-Bucket pattern from firefly-services-rate-limits. Each queue message is one generate call. Worker submits, polls (or relies on webhook), persists result.

Pattern: Multi-variation A/B funnel

For campaigns where you want choice:

  1. Submit with numVariations: 4 and 2-4 different seeds
  2. Persist all 4 outputs to your bucket
  3. Downstream selection logic (human or automated) picks 1
  4. Audit which combinations win for future prompt tuning

This is the standard pattern for key-art generation in enterprise campaign pipelines — variations give downstream creative teams options without re-running the pipeline.

Validate

A correctly wired V3 async pipeline:

  1. Submits jobs and persists jobId before any subsequent work
  2. Polls with 1-2s cadence, or uses webhook callbacks
  3. Honors statusUrl from the submission response — does not hardcode URLs
  4. Downloads result URLs within 1 hour and re-hosts in your own bucket
  5. Has retry-with-backoff on submission (covered by firefly-services-rate-limits)
  6. Logs jobId for every submission for downstream audit

Troubleshooting & Edge Cases

  • numVariations > 4 rejected: Max is 4. Submit multiple jobs if you need more.
  • Size rejected as invalid: Use only the published sizes (see Supported sizes table above).
  • prompt rejected as too long: Max 1024 chars. Strip or rephrase.
  • Webhook never fires: Adobe was unable to reach the URL. Test with a curl -X POST from outside your VPC. Fall back to polling.
  • Job stuck in running for >5 minutes: Cancel via cancelUrl and resubmit. Adobe-side jobs almost always complete in under 30s; 5+ minutes is a sign something is wrong.
  • outputs array is empty on success: Content safety filtered all variations. Rephrase the prompt — see firefly-services-troubleshoot §6.
  • Different output between identical requests: Set seeds: [<int>] for determinism.

Chaining with Other Skills

  • firefly-services-auth — Token freshness before submission
  • firefly-services-storage-refs — Required for any reference-image-based generation
  • firefly-services-rate-limits — Production batch pipeline
  • firefly-services-troubleshoot — When generation fails

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.