Download attachments via API

How to download attachments via API?
My code:
const fetch = require(‘node-fetch’);


async function addAttach(taskGid) {
    try {
        const attach = global.client.attachments.getAttachmentsForTask(taskGid, {opt_fields: "permanent_url"})
        .then((result) => {
            result.data.forEach((attach, name, index) => {
                setTimeout(() => download(attach.permanent_url), 300 * index)})
                 
   
        });
    } catch(e) {
        console.log(`Error addattach ${e}`);
    }
}

async function download(url) {
    const response = await fetch(url);
    const body = await response.body;
    return body
    
}

Response is:

<html><head><meta name="window" content="{&quot;clientVersion&quot;:&quot;c11912c228842823219b21d1bd6eeddae8b87791&quot;,&quot;recaptchaV2SiteKey&quot;:&quot;6LdkcuISAAAAAAbgBM_0ZV4dH2wVsq2Ck0jJRFdS&quot;,&quot;recaptchaEnterpriseSiteKey&quot;:&quot;6Lf1FtQZAAAAAKGsW3JoCdtQfzfCb2umWbEoyOjE&quot;,&quot;releaseRevision&quot;:&quot;20211123_221625_c11912c228842823219b21d1bd6eeddae8b87791&quot;,&quot;syncCluster&quot;:&quot;prod&quot;,&quot;userAgent&quot;:&quot;&quot;,&quot;loginConfig&quot;:{&quot;targetUrl&quot;:&quot;https://app.asana.com/app/asana/-/get_asset?asset_id=1201421973204556&quot;,&quot;signupUrl&quot;:&quot;https://asana.com/create-account&quot;,&quot;configName&quot;:&quot;prod&quot;,&quot;showSandLoginButtons&quot;:false,&quot;error&quot;:null,&quot;googleLastEmail&quot;:&quot;&quot;,&quot;googleSsoRequirements&quot;:[],&quot;samlTimeoutInfoIfLoginExpired&quot;:null,&quot;recentSamlEmail&quot;:null,&quot;recentSamlEmailDomain&quot;:null,&quot;forceEmail&quot;:&quot;&quot;,&quot;oauthActionUrl&quot;:&quot;https://app.asana.com/-/oauth2callback&quot;,&quot;extraGoogleAuthTokens&quot;:[],&quot;shareLinkKey&quot;:&quot;&quot;,&quot;shareLinkDomainId&quot;:&quot;&quot;,&quot;utm_campaign&quot;:&quot;&quot;,&quot;utm_medium&quot;:&quot;&quot;,&quot;utm_source&quot;:&quot;&quot;,&quot;enableCaptchas&quot;:true,&quot;showCaptchaCheckbox&quot;:false,&quot;showCaptchaScore&quot;:true,&quot;xsrfToken&quot;:&quot;5cbade8f16d57d8de4abdbb169d0f701:1637762036518&quot;,&quot;autoDetectLogin&quot;:false,&quot;translations&quot;:{&quot;14vqnjf&quot;:[0,&quot;Back&quot;],&quot;1jgonki&quot;:[0,&quot;Asana logo&quot;],&quot;6c7p6e&quot;:[2,&quot;This site is protected by reCAPTCHA and the Google &lt;privacylink&gt;Privacy Policy&lt;/privacylink&gt; and &lt;termslink&gt;Terms of Service&lt;/termslink&gt; apply.&quot;],&quot;1qwtlnx&quot;:[0,&quot;Log in with Google&quot;],&quot;1cymyzw&quot;:[0,&quot;or&quot;],&quot;5rjrso&quot;:[0,&quot;Don&#x27;t have an account?&quot;],&quot;rs0iwj&quot;:[0,&quot;Sign up&quot;],&quot;1pr4zrz&quot;:[0,&quot;Email address&quot;],&quot;1p2cgqu&quot;:[0,&quot;Password&quot;],&quot;xhq4e8&quot;:[0,&quot;Please enter a valid email address.&quot;],&quot;hem9g3&quot;:[0,&quot;Forgot your password?&quot;],&quot;r8w892&quot;:[0,&quot;Log in&quot;],&quot;ho39t5&quot;:[1,&quot;{googleSsoName} requires you to use a Google account from the {domain} domain.&quot;],&quot;1bf7ig9&quot;:[0,&quot;You must use a Google account from one of the following domains:&quot;],&quot;z65pvl&quot;:[0,&quot;Your account has been disabled. Please contact support@asana.com.&quot;],&quot;aua64b&quot;:[0,&quot;You can&#x27;t log in to Asana as this service account.&quot;],&quot;k6uh33&quot;:[0,&quot;Oops! It looks like that login link has expired. Try logging in again to continue.&quot;],&quot;14ezaf7&quot;:[0,&quot;Sorry, that password reset link has expired and cannot be used for the safety of your account.&quot;],&quot;1pwvtgo&quot;:[2,&quot;You can get another one &lt;a&gt;here&lt;/a&gt;.&quot;],&quot;1d1r0o5&quot;:[0,&quot;Sorry, that password reset link has already been used and cannot be used again for the safety of your account.&quot;],&quot;1prxpic&quot;:[0,&quot;Single sign-on (SSO) is not enabled for your account. Please log in with your email address and password below.&quot;],&quot;mxktt9&quot;:[0,&quot;Single sign-on (SAML) is not enabled for your account. To enable, please reach out to your admin. In the meantime, log in with your email address and password below.&quot;],&quot;1an7k9h&quot;:[0,&quot;Single sign-on (SAML) authentication failed. Please contact your domain administrator.&quot;],&quot;13yklei&quot;:[0,&quot;Single sign-on (SAML) authentication failed due to a certificate error. Please contact your domain administrator.&quot;],&quot;10mp3lq&quot;:[0,&quot;Google authentication failed. Please try again.&quot;],&quot;k1z2fc&quot;:[1,&quot;There isn&#x27;t an Asana account associated with {email}.&quot;],&quot;23eye4&quot;:[2,&quot;&lt;a&gt;Sign Up&lt;/a&gt;&quot;],&quot;5kueq5&quot;:[0,&quot;Please fill out the challenge below.&quot;],&quot;tbbnjv&quot;:[0,&quot;Your response to the challenge was incorrect. Please try again.&quot;],&quot;1t93f7g&quot;:[2,&quot;You may be having trouble logging in due to your location, we apologize for the inconvenience. Please &lt;a&gt;contact us&lt;/a&gt; if you require assistance.&quot;],&quot;1ks84ig&quot;:[0,&quot;A verification email has been sent to you. Please follow the instructions to log in.&quot;],&quot;9jsgm8&quot;:[0,&quot;Your password is invalid. Please use the Forgot your password link to reset it.&quot;],&quot;gur3f3&quot;:[0,&quot;An administrator in your Organization has reset your Asana password.&quot;],&quot;1a45du7&quot;:[0,&quot;Instructions to create a new password have been sent to the email associated with this account.&quot;],&quot;l4btgw&quot;:[0,&quot;That invitation has been canceled. Please ask to be re-invited.&quot;],&quot;rsebx2&quot;:[0,&quot;We believe that you&#x27;re attempting to create a domain that is in violation of our Terms of Service. If you believe this is an error, please contact terms-questions@asana.com.&quot;],&quot;k758tl&quot;:[0,&quot;That invitation is invalid. Did you copy the full URL?&quot;],&quot;zet2c7&quot;:[0,&quot;The username or password is not correct.&quot;],&quot;a37mos&quot;:[2,&quot;Did you &lt;forget&gt;forget your password?&lt;/forget&gt;&quot;],&quot;5ydeav&quot;:[0,&quot;Account belongs to multiple SSO-required domains.&quot;],&quot;188ugn5&quot;:[0,&quot;The code you entered is invalid. Please try again.&quot;],&quot;1ksycox&quot;:[0,&quot;You have attempted to enter your code too many times. Please try again later.&quot;],&quot;9w28sf&quot;:[0,&quot;Your password was successfully updated. Please log in.&quot;],&quot;cvuqjc&quot;:[0,&quot;An unexpected error occurred while updating your password. Please try again.&quot;],&quot;uv4et9&quot;:[0,&quot;You must be logged in to view this form&quot;],&quot;g709tu&quot;:[0,&quot;An unexpected error occurred, and we couldn&#x27;t log you in. Please try again in a few minutes.&quot;],&quot;1szzfwx&quot;:[0,&quot;Your administrator has disabled encryption keys and revoked Asana access. Please contact your administrator to restore access.&quot;],&quot;s2xel2&quot;:[0,&quot;Asana.com&quot;],&quot;1ush6q3&quot;:[0,&quot;Support&quot;],&quot;squ6rv&quot;:[0,&quot;Integrations&quot;],&quot;ak3oov&quot;:[0,&quot;Forum&quot;],&quot;11s3qgw&quot;:[0,&quot;Developers &amp; API&quot;],&quot;1hkszeb&quot;:[0,&quot;Resources&quot;],&quot;1gnsi5b&quot;:[0,&quot;Guide&quot;],&quot;1yooo5n&quot;:[0,&quot;Templates&quot;],&quot;hcswtb&quot;:[0,&quot;Pricing&quot;],&quot;hnou9x&quot;:[0,&quot;Terms&quot;],&quot;bfeasx&quot;:[0,&quot;Privacy&quot;],&quot;1uwrwno&quot;:[0,&quot;Confirm login&quot;],&quot;5h2oka&quot;:[1,&quot;You are about to log in to Asana as {email}&quot;],&quot;1ocf51d&quot;:[0,&quot;Log in with a different account&quot;],&quot;rhvlec&quot;:[1,&quot;For your security, {domain} logs you out of Asana after {numberOfUnitsOfTime} {unitsOfTime}. Please log in again.&quot;],&quot;15jcji0&quot;:[0,&quot;minutes&quot;],&quot;3830xi&quot;:[0,&quot;hours&quot;],&quot;yjc5xm&quot;:[0,&quot;days&quot;],&quot;16hmvgm&quot;:[0,&quot;seconds&quot;],&quot;wqm88m&quot;:[0,&quot;Two-factor authentication is enabled&quot;],&quot;u27l2b&quot;:[0,&quot;Continue to Asana&quot;],&quot;1twzobx&quot;:[0,&quot;Each time you log in with your email and password, you will be required to enter an authentication code.&quot;],&quot;1vvgnti&quot;:[0,&quot;An organization you belong to requires two-factor authentication&quot;],&quot;1hlp135&quot;:[0,&quot;Enable&quot;],&quot;ia0oya&quot;:[0,&quot;Scan this QR code&quot;],&quot;ivekyp&quot;:[0,&quot;Continue&quot;],&quot;1b5d4f4&quot;:[0,&quot;Enter authentication code&quot;],&quot;18kj80t&quot;:[0,&quot;This field can only contain numbers (e.g. 123456)&quot;],&quot;1gj9b8n&quot;:[1,&quot;Open your authentication app. If you don&#x27;t already have one, we recommend {duo}, {authy}, or {microsoftAuthenticator}.&quot;],&quot;zm0nie&quot;:[0,&quot;Twilio Authy&quot;],&quot;tte955&quot;:[0,&quot;Duo&quot;],&quot;1wcv93q&quot;:[0,&quot;Microsoft Authenticator&quot;],&quot;82rj3z&quot;:[0,&quot;Enter the 6-digit code generated by your authentication app.&quot;],&quot;1fczwp&quot;:[2,&quot;&lt;link&gt;What&#x27;s an authentication code?&lt;/link&gt;&quot;],&quot;ps80su&quot;:[1,&quot;Using the app, scan the QR code or manually enter the following code: {code}&quot;]},&quot;loginAppEmitGoogleTagManagerEvent&quot;:true,&quot;loginAppUseEmailFirstLogin&quot;:false}}"/><script nonce="r5w8gay5pguxow5sz3ipjbvnv">
                        window_attrs = JSON.parse(document.querySelector('meta[name="window"]').content);
                        window.global = window;
                        window.client_version = window_attrs.clientVersion;
                        window.recaptcha_v2_site_key = window_attrs.recaptchaV2SiteKey;
                        window.recaptcha_enterprise_site_key = window_attrs.recaptchaEnterpriseSiteKey;
                        window.release_revision = window_attrs.releaseRevision;
                        window.sync_cluster = window_attrs.syncCluster;
                        window.user_agent = window_attrs.userAgent;
                        window.loginConfig = window_attrs.loginConfig;
                    </script><link rel="shortcut icon" href="https://d3ki9tyy5l5ruj.cloudfront.net/obj/df5bcec7e9873dddebdd1328901c287f0f069750/asana-logo-favicon@3x.png"/><link href="https://d3ki9tyy5l5ruj.cloudfront.net/build/bundles/c11912c228842823219b21d1bd6eeddae8b87791/login/login.css" rel="stylesheet"/><script src="https://d3ki9tyy5l5ruj.cloudfront.net/build/bundles/c11912c228842823219b21d1bd6eeddae8b87791/login/login_javascript_bundle.js" type="text/javascript" defer="" nonce="r5w8gay5pguxow5sz3ipjbvnv"></script><meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/><meta name="apple-mobile-web-app-capable" content="yes"/><meta name="apple-mobile-web-app-status-bar-style" content="black"/><link rel="apple-touch-icon-precomposed" href="https://d3ki9tyy5l5ruj.cloudfront.net/obj/a6b9e5cd80c49ace4d4d1863febef31615d6c227/icon.png"/><link rel="canonical" href="https://app.asana.com/-/login"/><style type="text/css">.grecaptcha-badge { visibility: hidden; }</style></head><body><div id="Login-appRoot"><title>Log in - Asana</title><div class="LoginCardLayout"><div class="LoginCardLayout-card--withCaptchaNotice LoginCardLayout-card"><div class="LoginCardLayout-backButtonContainer"></div><a class="LoginCardLayout-asanaLogoContainer PrimaryLink BaseLink" href="/-/login_page_log?se=%7B%22action%22%3A%22LinkClicked%22%2C%22sub_action%22%3A%22AsanaLogo%22%2C%22location%22%3A%22LoginPage%22%2C%22url%22%3A%22https%3A%2F%2Fasana.com%2F%22%2C%22name%22%3A%22LinkClicked%22%2C%22non_user_action_event%22%3Afalse%7D&amp;allowed=login_page&amp;dest=https%3A%2F%2Fasana.com%2F"><img class="LoginCardLayout-asanaLogo" src="https://d3ki9tyy5l5ruj.cloudfront.net/obj/6c76122a923c696febd26d2a843aa6daf960a3df/logo.png" alt="Asana logo"/></a><div class="LoginDefaultView-content"><div><!-- DEBUG-TAG: login --></div><div name="login" id="debug_page_load_marker"></div><div class="AbstractThemeableRectangularButton--isEnabled AbstractThemeableRectangularButton AbstractThemeableRectangularButton--xlarge SecondaryButton GoogleSignInButton--sparse GoogleSignInButton LoginDefaultView-ssoButton" role="button" tabindex="0"><svg class="AbstractThemeableRectangularButton-leftIcon GoogleSignInButton-logo--sparse GoogleSignInButton-logo" viewBox="0 0 18 18"><path d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z" fill="#4285F4"></path><path d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z" fill="#34A853"></path><path d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z" fill="#FBBC05"></path><path d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z" fill="#EA4335"></path></svg>Log in with Google</div><span class="SeparatorRow LoginDefaultView-separatorRow"><span class="SeparatorRow-horizontalLine"></span><span class="SeparatorRow-label LoginDefaultView-separatorRowLabel">or</span><span class="SeparatorRow-horizontalLine"></span></span><form class="LoginEmailPasswordForm"><div class="LoginEmailPasswordForm-emailAndPassword"><span class="Typography Typography--colorDarkGray1 Typography--s">Email address</span><div><div class="ValidatedInput"><input type="text" class="TextInputBase SizedTextInput SizedTextInput--medium TextInput LoginEmailPasswordForm-emailInput" name="e" value="" autoComplete="username" autofocus="" services="[object Object]"/></div></div><span class="LoginEmailPasswordForm-passwordText Typography Typography--colorDarkGray1 Typography--s">Password</span><div><div class="ValidatedInput"><input type="password" class="TextInputBase SizedTextInput SizedTextInput--medium TextInput LoginEmailPasswordForm-passwordInput" name="p" value="" autoComplete="current-password" services="[object Object]"/></div></div></div><a class="LoginEmailPasswordForm-forgotPassword SecondaryLink BaseLink" href="/-/login_page_log?se=%7B%22action%22%3A%22LinkClicked%22%2C%22sub_action%22%3A%22ForgotPassword%22%2C%22location%22%3A%22LoginPage%22%2C%22url%22%3A%22%2F-%2Fforgot_password%22%2C%22name%22%3A%22LinkClicked%22%2C%22non_user_action_event%22%3Afalse%7D&amp;allowed=login_page&amp;dest=%2F-%2Fforgot_password"><span class="Typography Typography--s">Forgot your password?</span></a><div class="AbstractThemeableRectangularButton--isEnabled AbstractThemeableRectangularButton AbstractThemeableRectangularButton--large NuxButton LoginEmailPasswordForm-logInButton" role="button" aria-disabled="false" tabindex="0">Log in</div></form><div class="LoginDefaultView-signUp"><div><!-- DEBUG-TAG: login-signup --></div><span class="Typography Typography--colorDarkGray1 Typography--m">Don&#x27;t have an account?</span><a class="LoginDefaultView-signUpButtonLink PrimaryLink BaseLink" href="/-/login_page_log?se=%7B%22action%22%3A%22LinkClicked%22%2C%22sub_action%22%3A%22SignUp%22%2C%22location%22%3A%22LoginPage%22%2C%22url%22%3A%22https%3A%2F%2Fasana.com%2Fcreate-account%22%2C%22name%22%3A%22LinkClicked%22%2C%22non_user_action_event%22%3Afalse%7D&amp;allowed=login_page&amp;dest=https%3A%2F%2Fasana.com%2Fcreate-account">Sign up</a></div></div><span class="LoginCardLayout-captchaNotice Typography Typography--colorDarkGray1 Typography--s Typography--textAlignCenter">This site is protected by reCAPTCHA and the Google <a target="_blank" rel="noreferrer noopener" class="SecondaryLink BaseLink" href="https://policies.google.com/privacy">Privacy Policy</a> and <a target="_blank" rel="noreferrer noopener" class="SecondaryLink BaseLink" href="https://policies.google.com/terms">Terms of Service</a> apply.</span></div><div class="LoginFooter"><nav><ul class="LoginFooter-navRow"><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/"><span class="Typography Typography--m">Asana.com</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/support"><span class="Typography Typography--m">Support</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/apps"><span class="Typography Typography--m">Integrations</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://forum.asana.com"><span class="Typography Typography--m">Forum</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://developers.asana.com"><span class="Typography Typography--m">Developers &amp; API</span></a></li></ul><ul class="LoginFooter-navRow"><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/resources"><span class="Typography Typography--m">Resources</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/guide"><span class="Typography Typography--m">Guide</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/templates"><span class="Typography Typography--m">Templates</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/pricing"><span class="Typography Typography--m">Pricing</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/terms"><span class="Typography Typography--m">Terms</span></a></li><li class="LoginFooter-navRowItem"><a class="HiddenLink HiddenLink--darkGray1 BaseLink" href="https://asana.com/terms#privacy-policy"><span class="Typography Typography--m">Privacy</span></a></li></ul></nav></div></div></div></body></html>

Try to use the “download_url” instead of “permanent_url”.

Download_url is a temporary secure download link, that works for a few minutes or seconds.

So, I also suggestion to query that url just before downloading the file, using:
GET /attachments/{ID_ATTACHMENT}
because, if you query download_url for 1000 tasks, and you start download them one by one, some url may expire before you start your download.

If you try to download them from the browser client-side, you will certainly get CORS cross origin error.
You should download them from server-side, but I think that’s what you do, using “node-fetch”.

1 Like

When I get an attachment via API, both download_url and permanent_url are None / empty regardless of whether I uploaded the attachment via API or UI. Is anyone else facing this?