yikart/AiToEarn · error · PinterestPlatformException

15070

15070

Error message

{{platform}} platform API request failed

What it means

PinterestService's axios interceptor converts every failed Pinterest API request into a PinterestPlatformException with ResponseCode.ChannelPlatformApiFailed (15070). Network-level Axios failures (no response) and non-2xx responses from api.pinterest.com are both wrapped, with the Pinterest error body parsed for platform code/message. The exception records endpoint, method, HTTP status, category, retryability, and the raw platform payload.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/pinterest/pinterest.service.ts:45

  PinterestPinMediaSourceType,
} from './pinterest.interface'

@Injectable()
export class PinterestService {
  private readonly http: AxiosInstance
  private readonly apiBaseUrl: string

  constructor(private readonly cfg: PinterestConfig) {
    this.apiBaseUrl = this.normalizeApiBaseUrl(cfg.baseUrl)
    this.http = this.createHttpClient()
  }

  private createHttpClient(): AxiosInstance {
    const http = axios.create()
    http.interceptors.response.use(
      response => response,
      (error: AxiosError<PinterestErrorBody>) => {
        throw PinterestPlatformException.fromAxiosError(error)
      },
    )
    return http
  }

  private normalizeApiBaseUrl(baseUrl: string): string {
    const trimmed = (baseUrl || 'https://api.pinterest.com').replace(/\/+$/, '')
    return trimmed.endsWith('/v5') ? trimmed : `${trimmed}/v5`
  }

  generateAuthUrl(scopes: string[], state: string): string {
    const params = new URLSearchParams({
      client_id: this.cfg.clientId,
      redirect_uri: this.cfg.redirectUri,
      scope: scopes.join(','),
      state,
      response_type: 'code',
    })

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Read the thrown PinterestPlatformException's cause (platformCode, platformMessage, httpStatus) for Pinterest's specific error detail.
  2. If httpStatus is 401, refresh the Pinterest access token or re-run the OAuth flow, then retry.
  3. If 403, verify the token scopes cover pin creation and that the target board belongs to the authenticated account.
  4. If 429 or a retryable network/5xx error, retry with exponential backoff honoring rate-limit headers.
  5. Validate pin payload fields (board_id, media source, link URL) against the Pinterest v5 schema and confirm base URL handling via normalizeApiBaseUrl is correct.

Example fix

// before: expired token -> 401 wrapped as 15070
const res = await this.http.post(`${this.apiBaseUrl}/v5/pins`, pinBody, { headers: { Authorization: `Bearer ${token}` } })

// after: refresh on 401 and retry
try {
  const res = await this.http.post(`${this.apiBaseUrl}/v5/pins`, pinBody, { headers: { Authorization: `Bearer ${token}` } })
} catch (e) {
  if (e instanceof PinterestPlatformException && e.cause?.httpStatus === 401) {
    const fresh = await this.refreshAccessToken(channel)
    const res = await this.http.post(`${this.apiBaseUrl}/v5/pins`, pinBody, { headers: { Authorization: `Bearer ${fresh}` } })
  } else throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: token freshness and board ownership check
if (!accessToken || tokenExpiresAt <= new Date()) await refreshPinterestToken(channel)
const boards = await pinterestService.listBoards(accessToken)
if (!boards.some(b => b.id === targetBoardId)) throw new Error(`Board ${targetBoardId} not accessible for this Pinterest account`)

Type guard

function isPinterestPlatformException(e: unknown): e is PinterestPlatformException {
  return e instanceof PinterestPlatformException
}

Try / catch

try {
  const result = await pinterestService.createPin(accessToken, pinPayload)
} catch (e) {
  if (e instanceof PinterestPlatformException) {
    if (e.cause?.httpStatus === 401) {
      // refresh token and retry once
    } else if (e.retryable) {
      // exponential backoff (429 / 5xx / network)
    } else {
      // log cause.platformCode/platformMessage and notify the channel owner
    }
  } else throw e
}

Prevention

When it happens

Trigger: Any request through this.http — OAuth token exchange, fetching user boards, creating pins/media uploads — that returns a non-2xx status (401 invalid token, 403 forbidden board/account, 429 rate limit, 400 invalid pin data) or fails before a response arrives (timeout, DNS, connection refused, TLS failure).

Common situations: Pinterest access token expired or user revoked authorization; app missing ads/pins write scopes; publishing to a board the user no longer owns; Pinterest v5 API breaking changes after version bumps; rate limiting during bulk pin creation; server egress blocked to api.pinterest.com.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/8685632fca549795. Report an issue: GitHub.