arazzo: 1.0.1 info: title: Onboard a guest and send a welcome message version: 1.0.0 description: >- Check the account has credit, create a guest contact in Cendyn CRM (PUSHTech) with GDPR consent recorded, subscribe them to an audience list, send a welcome email, and confirm the delivery reached a terminal status. Authored by API Evangelist against the derived OpenAPI; every operationId is verified to exist in that spec. sourceDescriptions: - name: cendynCrm url: ../openapi/push-cendyn-crm-openapi.yml type: openapi workflows: - workflowId: onboardAndWelcomeGuest summary: Create a guest contact, subscribe them to a list, send a welcome email and confirm delivery. description: >- NOTE — this API publishes no idempotency contract. If step sendWelcomeEmail fails with a network error, do NOT re-run the workflow: run listDeliveries filtered by contact_id first to determine whether the message was already sent, or the guest receives it twice and the account is charged twice. inputs: type: object required: [accountId, userId, email, firstName, lastName] properties: accountId: type: string description: The Cendyn CRM account id. Not derivable from the token — it must be supplied. userId: type: string description: Your own backend identifier for this guest. The documented de-duplication key. email: type: string description: Guest email. Either email or phone_countrycode plus phone_number is required. firstName: {type: string} lastName: {type: string} language: type: string description: ISO-2 language code, e.g. ES. default: EN gdprMarketingConsent: {type: boolean, default: false} gdprDate: type: string description: ISO 8601 timestamp of when consent was given. gdprRemoteIp: type: string description: The IP address the guest consented from. welcomeSubject: {type: string, default: Welcome} steps: - stepId: checkBalance description: >- Confirm the account has credit before doing anything that spends it. Deliveries and email validation draw this balance down; there is no documented behaviour at zero. operationId: currentBalanceAccount parameters: - name: account_id in: path value: $inputs.accountId successCriteria: - condition: $statusCode == 200 outputs: balance: $response.body#/balance currency: $response.body#/currency - stepId: findExistingContact description: >- Look the guest up by your own user_id before creating them. There is no idempotency on createContact, so this check is the only thing standing between a retry and a duplicate guest record. operationId: listContact parameters: - name: account_id in: path value: $inputs.accountId - name: user_id in: query value: $inputs.userId successCriteria: - condition: $statusCode == 200 - stepId: createContact description: >- Create the guest with consent recorded in the same call. Returns 201 with the contact id — a 24-character hex string that is the handle for every downstream step. operationId: createContact parameters: - name: account_id in: path value: $inputs.accountId requestBody: contentType: application/json payload: user_id: $inputs.userId email: $inputs.email name_first: $inputs.firstName name_last: $inputs.lastName language: $inputs.language gdpr_marketing_consent: $inputs.gdprMarketingConsent gdpr_accept_terms: true gdpr_date: $inputs.gdprDate gdpr_remote_ip: $inputs.gdprRemoteIp successCriteria: - condition: $statusCode == 201 outputs: contactId: $response.body#/id - stepId: listAudienceLists description: Find the audience list to subscribe the new guest to. operationId: listAudienceList parameters: - name: account_id in: path value: $inputs.accountId successCriteria: - condition: $statusCode == 200 - stepId: sendWelcomeEmail description: >- CONSEQUENTIAL STEP — this reaches a real person and spends real credit, and it is not idempotent. Gate it behind confirmation in any autonomous loop. operationId: sendEmailDeliveries parameters: - name: account_id in: path value: $inputs.accountId requestBody: contentType: application/json payload: contact_id: $steps.createContact.outputs.contactId subject: $inputs.welcomeSubject successCriteria: - condition: $statusCode == 200 - stepId: confirmDelivery description: >- Follow the message to a terminal status. Match on "deliverd" — the misspelling is in the provider's own published status vocabulary. "undefined" is also a real value in that enum. operationId: listDeliveries parameters: - name: account_id in: path value: $inputs.accountId - name: contact_id in: query value: $steps.createContact.outputs.contactId successCriteria: - condition: $statusCode == 200 outputs: contactId: $steps.createContact.outputs.contactId balanceBefore: $steps.checkBalance.outputs.balance