โœฆ Portfolio Project ยท Jay Yoon

Brand assets,
synced automatically

An event-driven integration between AWS S3 and Canva โ€” so marketing teams stop manually uploading files and start focusing on what they actually love doing.

๐Ÿ”„ Live Sync Pipeline
Active
๐Ÿชฃ
S3 Bucket โ€” brand asset upload P1
โšก
EventBridge โ€” PutObject trigger P1
๐Ÿค–
Bedrock โ€” AI metadata tagging P2
ฮป
Lambda โ€” upload handler P1
๐ŸŽจ
Canva Connect API โ€” asset live P1

Built for real enterprise workflows

Large marketing teams manage thousands of approved assets in S3. This eliminates the manual middle step.

โšก

Event-driven, zero manual work

S3 PutObject fires EventBridge, which triggers Lambda, which calls Canva โ€” all without a human touching anything.

๐Ÿค–

AI tagging that actually means something

Amazon Bedrock generates business-context tags like brand_tier: premium and approved_for: social_media โ€” not just "beach" or "sunset".

๐Ÿ”„

It goes both ways

Canva Webhooks push finished designs back to S3. The loop closes automatically โ€” from raw asset to published design and back.

How it's built

Serverless, event-driven AWS infrastructure + Canva Connect API. Toggle between phases.

S3 โ†’ EventBridge โ†’ Lambda โ†’ Canva Connect API
S3 Bucket
Asset store
โ†’
PutObject
EventBridge
Event rule
โ†’
invoke
Lambda
Upload handler
โ†’
HTTP POST
Canva API
Upload + poll
โ†’
logs
CloudWatch
Observability
Forward: S3 โ†’ Bedrock โ†’ Lambda โ†’ Canva ยท Reverse: Canva โ†’ API Gateway โ†’ S3
Forward Sync โ€” S3 โ†’ Canva
S3 Bucket
Asset store
โ†’
PutObject
EventBridge
Event rule
โ†’
invoke
Bedrock
AI tagging
โ†’
tags
Lambda
Upload handler
โ†’
HTTP POST
Canva API
Upload + poll
โ†’
store
DynamoDB
Metadata
Reverse Sync โ€” Canva โ†’ S3
React Dashboard
Export trigger
โ†’
POST
API Gateway
JWT auth
โ†’
invoke
Lambda
Export handler
โ†’
export
Canva API
Design export
โ†’
save
S3 Bucket
canva-exports/
Auth Layer
React (Vite)
Dashboard UI
โ†’
PKCE
Cognito
User Pool
โ†’
JWT
API Gateway
Authorizer
โ†’
logs
CloudWatch
Observability

๐Ÿชฃ S3 Trigger

  • PutObject event fires on image upload
  • EventBridge rule filters by prefix & suffix
  • Supports PNG, JPG, SVG

ฮป Lambda Handler

  • Generates presigned URL for S3 object
  • Initiates Canva async upload job
  • Polls until status = success

๐ŸŽจ Canva Integration

  • OAuth 2.0 PKCE token flow
  • Async upload + job polling pattern
  • Asset lands in Canva Brand Kit

๐Ÿ“Š Observability

  • CloudWatch structured logging
  • Success / failure metrics
  • Lambda duration tracking

Endpoints

Backend REST API (API Gateway + Lambda) plus key Canva Connect API endpoints.

โœ… Phase 1 โ€” Implemented ยท Canva Connect API (S3 โ†’ Canva Pipeline)
POST /v1/asset-uploads Initiate async asset upload (returns job ID) โ–พ
Auth โ€” OAuth 2.0 PKCE ยท Scope: asset:write
HeaderValue
AuthorizationBearer {access_token}
Asset-Upload-Metadata JSON with name_base64 โ€” filename Base64-encoded
e.g. {"name_base64": "b2NlYW5fMS5wbmc="}
Content-Typeapplication/octet-stream
Example Response
{
    "job": {
      "id": "ded634f4-650c-4a16-8317-06ae058731fd",
      "status": "in_progress"
    }
}
GET /v1/asset-uploads/{job_id} Poll upload job until complete โ–พ
Polling Pattern (Python โ€” Lambda)
# Poll every 3s until status == "success" (max 10 attempts)
  for attempt in range(10):
      time.sleep(3)
      res = canva_get(f"/v1/asset-uploads/{job_id}")
      status = res["job"]["status"]
      if status == "success":
          asset_id = res["job"]["asset"]["id"]
          break
      elif status == "failed":
          raise Exception("Upload failed")
Example Success Response
{
    "job": {
      "id": "ded634f4-650c-4a16-8317-06ae058731fd",
      "status": "success",
      "asset": {
        "id": "MAHDuGNfRwQ",
        "name": "snowy_mountain_3.jpg",
        "type": "image"
        ...: "..."
        }
    }
}
GET /v1/users/me Validate token + retrieve user profile โ–พ
Used for OAuth token validation (Day 1)
{
    "team_user": {
        "user_id": "oUgaASGNYnVze12572GURM",
        "team_id": "oBZGQiMnI_1xoCp&NSB2GQ"
    }
}
โœ… Phase 2 โ€” Implemented ยท Backend REST API (API Gateway + Lambda)
POST /sync/trigger Manually trigger S3 โ†’ Canva sync โ–พ
Request Body
ParameterTypeRequiredDescription
s3_bucketstringrequiredSource S3 bucket name
s3_keystringrequiredObject key (path to asset)
canva_folder_idstringoptionalTarget Canva folder โ€” defaults to root
ai_taggingbooleanoptionalEnable Bedrock tagging before upload
Example Response
{
    "job_id": "sync_a1b2c3d4",
    "status": "PROCESSING",
    "canva_upload_job_id": "CUJ_xxxxx",
    "ai_tags_generated": true
}
GET /sync/status/{job_id} Poll sync job status and result โ–พ
Example Response
{
    "job_id": "sync_a1b2c3d4",
    "status": "COMPLETE",
    "canva_asset_id": "BABXy1234",
    "ai_tags": {
      "brand_tier": "premium",
      "campaign_type": "seasonal",
      "approved_for": ["social_media", "web"]
    }
}
GET /assets List all synced assets from DynamoDB โ–พ
Query Parameters
ParameterTypeRequiredDescription
statusstringoptionalFilter: COMPLETE | PROCESSING | FAILED
limitintegeroptionalMax records. Default: 50
next_tokenstringoptionalPagination cursor
POST /webhooks/canva Canva Webhook receiver โ€” triggers reverse sync โ–พ
Canva Webhook Payload
{
    "event_type": "DESIGN_PUBLISH",
    "design_id": "DAF1234abcd",
    "export_url": "https://export.canva.com/...",
    "timestamp": "2025-06-01T12:30:00Z"
}
โœ… Phase 2 โ€” Implemented ยท Amazon Bedrock (AI Tagging โ€” Claude Haiku 4.5)
POST bedrock-runtime ยท invoke_model Business-context metadata generation before upload โ–พ
Why Bedrock? (vs Canva Smart Tags)
CapabilityCanva Smart TagsBedrock (planned)
Visual labels (beach, sunset)โœ“ Yesโœ“ Yes
Brand tier classificationโœ—โœ“ Custom schema
Campaign suitability tagsโœ—โœ“ Prompt-engineered
Pre-upload processingโœ— Post-upload onlyโœ“ Runs before upload
Customisable schemaโœ—โœ“ Fully customisable

Try it yourself

Simulate the full pipeline without live AWS credentials โ€” watch the event trace as a sample.

Trigger a sync

Fill in the details and fire the pipeline.

โœ… Sync complete!

Pipeline log

Live event trace as it flows through AWS โ†’ Canva.

Waiting for trigger...